diff --git a/.eslintrc.js b/.eslintrc.js index 3c173e5244009..2ce6d279d93a9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -69,26 +69,6 @@ module.exports = { 'jsx-a11y/no-onchange': 'off', }, }, - { - files: ['src/legacy/core_plugins/expressions/**/*.{js,ts,tsx}'], - rules: { - 'react-hooks/exhaustive-deps': 'off', - }, - }, - { - files: [ - 'src/legacy/core_plugins/vis_default_editor/public/components/controls/**/*.{ts,tsx}', - ], - rules: { - 'react-hooks/exhaustive-deps': 'off', - }, - }, - { - files: ['src/legacy/ui/public/vis/**/*.{js,ts,tsx}'], - rules: { - 'react-hooks/exhaustive-deps': 'off', - }, - }, { files: ['src/plugins/es_ui_shared/**/*.{js,ts,tsx}'], rules: { diff --git a/.i18nrc.json b/.i18nrc.json index c293b3103a39c..19d361aed9344 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -24,6 +24,7 @@ "src/legacy/core_plugins/management", "src/plugins/management" ], + "indexPatternManagement": "src/plugins/index_pattern_management", "advancedSettings": "src/plugins/advanced_settings", "kibana_legacy": "src/plugins/kibana_legacy", "kibana_react": "src/legacy/core_plugins/kibana_react", @@ -43,7 +44,7 @@ "tileMap": "src/legacy/core_plugins/tile_map", "timelion": ["src/legacy/core_plugins/timelion", "src/legacy/core_plugins/vis_type_timelion", "src/plugins/timelion"], "uiActions": "src/plugins/ui_actions", - "visDefaultEditor": "src/legacy/core_plugins/vis_default_editor", + "visDefaultEditor": "src/plugins/vis_default_editor", "visTypeMarkdown": "src/legacy/core_plugins/vis_type_markdown", "visTypeMetric": "src/legacy/core_plugins/vis_type_metric", "visTypeTable": "src/legacy/core_plugins/vis_type_table", diff --git a/docs/apm/images/service-maps-java.png b/docs/apm/images/service-maps-java.png new file mode 100644 index 0000000000000..e1a42f4c76e12 Binary files /dev/null and b/docs/apm/images/service-maps-java.png differ diff --git a/docs/apm/images/service-maps.png b/docs/apm/images/service-maps.png new file mode 100644 index 0000000000000..454ae9bb720fb Binary files /dev/null and b/docs/apm/images/service-maps.png differ diff --git a/docs/apm/service-maps.asciidoc b/docs/apm/service-maps.asciidoc new file mode 100644 index 0000000000000..e0d84f33b4dcb --- /dev/null +++ b/docs/apm/service-maps.asciidoc @@ -0,0 +1,48 @@ +[[service-maps]] +=== Service maps + +beta::[] + +A service map is a real-time diagram of the interactions occurring in your application’s architecture. +It allows you to easily visualize data flow and high-level statistics, like average transaction duration, +requests per minute, errors per minute, and metrics, allowing you to quickly assess the status of your services. + +Our beta offering creates two types of service maps: + +* Global: All services and connections are shown. +* Service-specific: Selecting a specific service will highlight it's connections. + +[role="screenshot"] +image::apm/images/service-maps.png[Example view of service maps in the APM app in Kibana] + +[float] +[[visualize-your-architecture]] +=== Visualize your architecture + +Select the **Service Map** tab to get started. +By default, all services and connections are shown. +Whether your onboarding a new engineer, or just trying to grasp the big picture, +click around, zoom in and out, and begin to visualize how your services are connected. + +If there's a specific service that interests you, select that service to highlight its connections. +Clicking **Focus map** will refocus the map on that specific service and lock the connection highlighting. +From here, select **Service Details**, or click on the **Transaction** tab to jump to the Transaction overview. +You can also use the tabs at the top of the page to easily jump to the **Errors** or **Metrics** overview. + +While it's not possible to query in service maps, it is possible to filter by environment. +This can be useful if you have two or more services, in separate environments, but with the same name. +Use the environment drop down to only see the data you're interested in, like `dev` or `production`. + +[role="screenshot"] +image::apm/images/service-maps-java.png[Example view of service maps with Java highlighted in the APM app in Kibana] + +[float] +[[service-maps-legend]] +=== Legend + +Nodes appear on the map in one of two shapes: + +* **Circle**: Instrumented services. Interior icons are based on the language of the agent used. +* **Diamond**: Databases, external, and messaging. Interior icons represent the generic type, +with specific icons for known entities, like Elasticsearch. +Type and subtype are based on `span.type`, and `span.subtype`. diff --git a/docs/apm/using-the-apm-ui.asciidoc b/docs/apm/using-the-apm-ui.asciidoc index 1361dc046e3b1..b1b7ed7307986 100644 --- a/docs/apm/using-the-apm-ui.asciidoc +++ b/docs/apm/using-the-apm-ui.asciidoc @@ -31,6 +31,8 @@ include::transactions.asciidoc[] include::spans.asciidoc[] +include::service-maps.asciidoc[] + include::errors.asciidoc[] include::metrics.asciidoc[] diff --git a/docs/developer/plugin/development-plugin-feature-registration.asciidoc b/docs/developer/plugin/development-plugin-feature-registration.asciidoc index ca61e5309ce85..4702204196bf2 100644 --- a/docs/developer/plugin/development-plugin-feature-registration.asciidoc +++ b/docs/developer/plugin/development-plugin-feature-registration.asciidoc @@ -45,10 +45,15 @@ Registering a feature consists of the following fields. For more information, co |An array of applications this feature enables. Typically, all of your plugin's apps (from `uiExports`) will be included here. |`privileges` (required) -|{repo}blob/{branch}/x-pack/plugins/features/server/feature.ts[`FeatureWithAllOrReadPrivileges`]. +|{repo}blob/{branch}/x-pack/plugins/features/common/feature.ts[`FeatureConfig`]. |See <> and <> |The set of privileges this feature requires to function. +|`subFeatures` (optional) +|{repo}blob/{branch}/x-pack/plugins/features/common/feature.ts[`FeatureConfig`]. +|See <> +|The set of subfeatures that enables finer access control than the `all` and `read` feature privileges. These options are only available in the Gold subscription level and higher. + |`icon` |`string` |"discoverApp" @@ -192,3 +197,78 @@ server.route({ } }); ----------- + +[[example-3-discover]] +==== Example 3: Discover + +Discover takes advantage of subfeature privileges to allow fine-grained access control. In this example, +a single "Create Short URLs" subfeature privilege is defined, which allows users to grant access to this feature without having to grant the `all` privilege to Discover. In other words, you can grant `read` access to Discover, and also grant the ability to create short URLs. + +["source","javascript"] +----------- +init(server) { + const xpackMainPlugin = server.plugins.xpack_main; + xpackMainPlugin.registerFeature({ + { + id: 'discover', + name: i18n.translate('xpack.features.discoverFeatureName', { + defaultMessage: 'Discover', + }), + order: 100, + icon: 'discoverApp', + navLinkId: 'kibana:discover', + app: ['kibana'], + catalogue: ['discover'], + privileges: { + all: { + app: ['kibana'], + catalogue: ['discover'], + savedObject: { + all: ['search', 'query'], + read: ['index-pattern'], + }, + ui: ['show', 'save', 'saveQuery'], + }, + read: { + app: ['kibana'], + catalogue: ['discover'], + savedObject: { + all: [], + read: ['index-pattern', 'search', 'query'], + }, + ui: ['show'], + }, + }, + subFeatures: [ + { + name: i18n.translate('xpack.features.ossFeatures.discoverShortUrlSubFeatureName', { + defaultMessage: 'Short URLs', + }), + privilegeGroups: [ + { + groupType: 'independent', + privileges: [ + { + id: 'url_create', + name: i18n.translate( + 'xpack.features.ossFeatures.discoverCreateShortUrlPrivilegeName', + { + defaultMessage: 'Create Short URLs', + } + ), + includeIn: 'all', + savedObject: { + all: ['url'], + read: [], + }, + ui: ['createShortUrl'], + }, + ], + }, + ], + }, + ], + } + }); +} +----------- diff --git a/docs/development/core/server/kibana-plugin-core-server.coresetup.md b/docs/development/core/server/kibana-plugin-core-server.coresetup.md index 29fdc37a81176..c10b460da8b4f 100644 --- a/docs/development/core/server/kibana-plugin-core-server.coresetup.md +++ b/docs/development/core/server/kibana-plugin-core-server.coresetup.md @@ -23,6 +23,7 @@ export interface CoreSetupHttpServiceSetup | [HttpServiceSetup](./kibana-plugin-core-server.httpservicesetup.md) | | [metrics](./kibana-plugin-core-server.coresetup.metrics.md) | MetricsServiceSetup | [MetricsServiceSetup](./kibana-plugin-core-server.metricsservicesetup.md) | | [savedObjects](./kibana-plugin-core-server.coresetup.savedobjects.md) | SavedObjectsServiceSetup | [SavedObjectsServiceSetup](./kibana-plugin-core-server.savedobjectsservicesetup.md) | +| [status](./kibana-plugin-core-server.coresetup.status.md) | StatusServiceSetup | [StatusServiceSetup](./kibana-plugin-core-server.statusservicesetup.md) | | [uiSettings](./kibana-plugin-core-server.coresetup.uisettings.md) | UiSettingsServiceSetup | [UiSettingsServiceSetup](./kibana-plugin-core-server.uisettingsservicesetup.md) | | [uuid](./kibana-plugin-core-server.coresetup.uuid.md) | UuidServiceSetup | [UuidServiceSetup](./kibana-plugin-core-server.uuidservicesetup.md) | diff --git a/docs/development/core/server/kibana-plugin-core-server.coresetup.status.md b/docs/development/core/server/kibana-plugin-core-server.coresetup.status.md new file mode 100644 index 0000000000000..f5ea627a9f008 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.coresetup.status.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CoreSetup](./kibana-plugin-core-server.coresetup.md) > [status](./kibana-plugin-core-server.coresetup.status.md) + +## CoreSetup.status property + +[StatusServiceSetup](./kibana-plugin-core-server.statusservicesetup.md) + +Signature: + +```typescript +status: StatusServiceSetup; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.corestatus.elasticsearch.md b/docs/development/core/server/kibana-plugin-core-server.corestatus.elasticsearch.md new file mode 100644 index 0000000000000..b41e7020c38e9 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.corestatus.elasticsearch.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CoreStatus](./kibana-plugin-core-server.corestatus.md) > [elasticsearch](./kibana-plugin-core-server.corestatus.elasticsearch.md) + +## CoreStatus.elasticsearch property + +Signature: + +```typescript +elasticsearch: ServiceStatus; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.corestatus.md b/docs/development/core/server/kibana-plugin-core-server.corestatus.md new file mode 100644 index 0000000000000..3fde86a18c58b --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.corestatus.md @@ -0,0 +1,21 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CoreStatus](./kibana-plugin-core-server.corestatus.md) + +## CoreStatus interface + +Status of core services. + +Signature: + +```typescript +export interface CoreStatus +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [elasticsearch](./kibana-plugin-core-server.corestatus.elasticsearch.md) | ServiceStatus | | +| [savedObjects](./kibana-plugin-core-server.corestatus.savedobjects.md) | ServiceStatus | | + diff --git a/docs/development/core/server/kibana-plugin-core-server.corestatus.savedobjects.md b/docs/development/core/server/kibana-plugin-core-server.corestatus.savedobjects.md new file mode 100644 index 0000000000000..d554c6f70d720 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.corestatus.savedobjects.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [CoreStatus](./kibana-plugin-core-server.corestatus.md) > [savedObjects](./kibana-plugin-core-server.corestatus.savedobjects.md) + +## CoreStatus.savedObjects property + +Signature: + +```typescript +savedObjects: ServiceStatus; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.incompatiblenodes.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.incompatiblenodes.md new file mode 100644 index 0000000000000..f8a45fe9a5a9c --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.incompatiblenodes.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ElasticsearchStatusMeta](./kibana-plugin-core-server.elasticsearchstatusmeta.md) > [incompatibleNodes](./kibana-plugin-core-server.elasticsearchstatusmeta.incompatiblenodes.md) + +## ElasticsearchStatusMeta.incompatibleNodes property + +Signature: + +```typescript +incompatibleNodes: NodesVersionCompatibility['incompatibleNodes']; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.md new file mode 100644 index 0000000000000..2398410fa4b84 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ElasticsearchStatusMeta](./kibana-plugin-core-server.elasticsearchstatusmeta.md) + +## ElasticsearchStatusMeta interface + + +Signature: + +```typescript +export interface ElasticsearchStatusMeta +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [incompatibleNodes](./kibana-plugin-core-server.elasticsearchstatusmeta.incompatiblenodes.md) | NodesVersionCompatibility['incompatibleNodes'] | | +| [warningNodes](./kibana-plugin-core-server.elasticsearchstatusmeta.warningnodes.md) | NodesVersionCompatibility['warningNodes'] | | + diff --git a/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.warningnodes.md b/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.warningnodes.md new file mode 100644 index 0000000000000..7374ccd9e7fa8 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.elasticsearchstatusmeta.warningnodes.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ElasticsearchStatusMeta](./kibana-plugin-core-server.elasticsearchstatusmeta.md) > [warningNodes](./kibana-plugin-core-server.elasticsearchstatusmeta.warningnodes.md) + +## ElasticsearchStatusMeta.warningNodes property + +Signature: + +```typescript +warningNodes: NodesVersionCompatibility['warningNodes']; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.md b/docs/development/core/server/kibana-plugin-core-server.md index 793684c1b3796..accab9bf0cb36 100644 --- a/docs/development/core/server/kibana-plugin-core-server.md +++ b/docs/development/core/server/kibana-plugin-core-server.md @@ -66,6 +66,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [ContextSetup](./kibana-plugin-core-server.contextsetup.md) | An object that handles registration of context providers and configuring handlers with context. | | [CoreSetup](./kibana-plugin-core-server.coresetup.md) | Context passed to the plugins setup method. | | [CoreStart](./kibana-plugin-core-server.corestart.md) | Context passed to the plugins start method. | +| [CoreStatus](./kibana-plugin-core-server.corestatus.md) | Status of core services. | | [CustomHttpResponseOptions](./kibana-plugin-core-server.customhttpresponseoptions.md) | HTTP response parameters for a response with adjustable status code. | | [DeprecationAPIClientParams](./kibana-plugin-core-server.deprecationapiclientparams.md) | | | [DeprecationAPIResponse](./kibana-plugin-core-server.deprecationapiresponse.md) | | @@ -75,6 +76,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [ElasticsearchError](./kibana-plugin-core-server.elasticsearcherror.md) | | | [ElasticsearchServiceSetup](./kibana-plugin-core-server.elasticsearchservicesetup.md) | | | [ElasticsearchServiceStart](./kibana-plugin-core-server.elasticsearchservicestart.md) | | +| [ElasticsearchStatusMeta](./kibana-plugin-core-server.elasticsearchstatusmeta.md) | | | [EnvironmentMode](./kibana-plugin-core-server.environmentmode.md) | | | [ErrorHttpResponseOptions](./kibana-plugin-core-server.errorhttpresponseoptions.md) | HTTP response parameters | | [FakeRequest](./kibana-plugin-core-server.fakerequest.md) | Fake request object created manually by Kibana plugins. | @@ -101,6 +103,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [LoggerFactory](./kibana-plugin-core-server.loggerfactory.md) | The single purpose of LoggerFactory interface is to define a way to retrieve a context-based logger instance. | | [LogMeta](./kibana-plugin-core-server.logmeta.md) | Contextual metadata | | [MetricsServiceSetup](./kibana-plugin-core-server.metricsservicesetup.md) | APIs to retrieves metrics gathered and exposed by the core platform. | +| [NodesVersionCompatibility](./kibana-plugin-core-server.nodesversioncompatibility.md) | | | [OnPostAuthToolkit](./kibana-plugin-core-server.onpostauthtoolkit.md) | A tool set defining an outcome of OnPostAuth interceptor for incoming request. | | [OnPreAuthToolkit](./kibana-plugin-core-server.onpreauthtoolkit.md) | A tool set defining an outcome of OnPreAuth interceptor for incoming request. | | [OnPreResponseExtensions](./kibana-plugin-core-server.onpreresponseextensions.md) | Additional data to extend a response. | @@ -162,15 +165,18 @@ The plugin integrates with the core system via lifecycle events: `setup` | [SavedObjectsResolveImportErrorsOptions](./kibana-plugin-core-server.savedobjectsresolveimporterrorsoptions.md) | Options to control the "resolve import" operation. | | [SavedObjectsServiceSetup](./kibana-plugin-core-server.savedobjectsservicesetup.md) | Saved Objects is Kibana's data persistence mechanism allowing plugins to use Elasticsearch for storing and querying state. The SavedObjectsServiceSetup API exposes methods for registering Saved Object types, creating and registering Saved Object client wrappers and factories. | | [SavedObjectsServiceStart](./kibana-plugin-core-server.savedobjectsservicestart.md) | Saved Objects is Kibana's data persisentence mechanism allowing plugins to use Elasticsearch for storing and querying state. The SavedObjectsServiceStart API provides a scoped Saved Objects client for interacting with Saved Objects. | +| [SavedObjectStatusMeta](./kibana-plugin-core-server.savedobjectstatusmeta.md) | Meta information about the SavedObjectService's status. Available to plugins via [CoreSetup.status](./kibana-plugin-core-server.coresetup.status.md). | | [SavedObjectsType](./kibana-plugin-core-server.savedobjectstype.md) | | | [SavedObjectsTypeManagementDefinition](./kibana-plugin-core-server.savedobjectstypemanagementdefinition.md) | Configuration options for the [type](./kibana-plugin-core-server.savedobjectstype.md)'s management section. | | [SavedObjectsTypeMappingDefinition](./kibana-plugin-core-server.savedobjectstypemappingdefinition.md) | Describe a saved object type mapping. | | [SavedObjectsUpdateOptions](./kibana-plugin-core-server.savedobjectsupdateoptions.md) | | | [SavedObjectsUpdateResponse](./kibana-plugin-core-server.savedobjectsupdateresponse.md) | | +| [ServiceStatus](./kibana-plugin-core-server.servicestatus.md) | The current status of a service at a point in time. | | [SessionCookieValidationResult](./kibana-plugin-core-server.sessioncookievalidationresult.md) | Return type from a function to validate cookie contents. | | [SessionStorage](./kibana-plugin-core-server.sessionstorage.md) | Provides an interface to store and retrieve data across requests. | | [SessionStorageCookieOptions](./kibana-plugin-core-server.sessionstoragecookieoptions.md) | Configuration used to create HTTP session storage based on top of cookie mechanism. | | [SessionStorageFactory](./kibana-plugin-core-server.sessionstoragefactory.md) | SessionStorage factory to bind one to an incoming request | +| [StatusServiceSetup](./kibana-plugin-core-server.statusservicesetup.md) | API for accessing status of Core and this plugin's dependencies as well as for customizing this plugin's status. | | [StringValidationRegex](./kibana-plugin-core-server.stringvalidationregex.md) | StringValidation with regex object | | [StringValidationRegexString](./kibana-plugin-core-server.stringvalidationregexstring.md) | StringValidation as regex string | | [UiSettingsParams](./kibana-plugin-core-server.uisettingsparams.md) | UiSettings parameters defined by the plugins. | @@ -184,6 +190,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | Variable | Description | | --- | --- | | [kibanaResponseFactory](./kibana-plugin-core-server.kibanaresponsefactory.md) | Set of helpers used to create KibanaResponse to form HTTP response on an incoming request. Should be returned as a result of [RequestHandler](./kibana-plugin-core-server.requesthandler.md) execution. | +| [ServiceStatusLevels](./kibana-plugin-core-server.servicestatuslevels.md) | The current "level" of availability of a service. | | [validBodyOutput](./kibana-plugin-core-server.validbodyoutput.md) | The set of valid body.output | ## Type Aliases @@ -256,6 +263,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [SavedObjectsClientWrapperFactory](./kibana-plugin-core-server.savedobjectsclientwrapperfactory.md) | Describes the factory used to create instances of Saved Objects Client Wrappers. | | [SavedObjectsFieldMapping](./kibana-plugin-core-server.savedobjectsfieldmapping.md) | Describe a [saved object type mapping](./kibana-plugin-core-server.savedobjectstypemappingdefinition.md) field.Please refer to [elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html) For the mapping documentation | | [ScopeableRequest](./kibana-plugin-core-server.scopeablerequest.md) | A user credentials container. It accommodates the necessary auth credentials to impersonate the current user.See [KibanaRequest](./kibana-plugin-core-server.kibanarequest.md). | +| [ServiceStatusLevel](./kibana-plugin-core-server.servicestatuslevel.md) | A convenience type that represents the union of each value in [ServiceStatusLevels](./kibana-plugin-core-server.servicestatuslevels.md). | | [SharedGlobalConfig](./kibana-plugin-core-server.sharedglobalconfig.md) | | | [StartServicesAccessor](./kibana-plugin-core-server.startservicesaccessor.md) | Allows plugins to get access to APIs available in start inside async handlers. Promise will not resolve until Core and plugin dependencies have completed start. This should only be used inside handlers registered during setup that will only be executed after start lifecycle. | | [StringValidation](./kibana-plugin-core-server.stringvalidation.md) | Allows regex objects or a regex string | diff --git a/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.incompatiblenodes.md b/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.incompatiblenodes.md new file mode 100644 index 0000000000000..8e7298d28801c --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.incompatiblenodes.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [NodesVersionCompatibility](./kibana-plugin-core-server.nodesversioncompatibility.md) > [incompatibleNodes](./kibana-plugin-core-server.nodesversioncompatibility.incompatiblenodes.md) + +## NodesVersionCompatibility.incompatibleNodes property + +Signature: + +```typescript +incompatibleNodes: NodeInfo[]; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.iscompatible.md b/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.iscompatible.md new file mode 100644 index 0000000000000..82a4800a3b4b6 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.iscompatible.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [NodesVersionCompatibility](./kibana-plugin-core-server.nodesversioncompatibility.md) > [isCompatible](./kibana-plugin-core-server.nodesversioncompatibility.iscompatible.md) + +## NodesVersionCompatibility.isCompatible property + +Signature: + +```typescript +isCompatible: boolean; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.kibanaversion.md b/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.kibanaversion.md new file mode 100644 index 0000000000000..347f2d3474b11 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.kibanaversion.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [NodesVersionCompatibility](./kibana-plugin-core-server.nodesversioncompatibility.md) > [kibanaVersion](./kibana-plugin-core-server.nodesversioncompatibility.kibanaversion.md) + +## NodesVersionCompatibility.kibanaVersion property + +Signature: + +```typescript +kibanaVersion: string; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.md b/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.md new file mode 100644 index 0000000000000..6fcfacc3bc908 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.md @@ -0,0 +1,22 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [NodesVersionCompatibility](./kibana-plugin-core-server.nodesversioncompatibility.md) + +## NodesVersionCompatibility interface + +Signature: + +```typescript +export interface NodesVersionCompatibility +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [incompatibleNodes](./kibana-plugin-core-server.nodesversioncompatibility.incompatiblenodes.md) | NodeInfo[] | | +| [isCompatible](./kibana-plugin-core-server.nodesversioncompatibility.iscompatible.md) | boolean | | +| [kibanaVersion](./kibana-plugin-core-server.nodesversioncompatibility.kibanaversion.md) | string | | +| [message](./kibana-plugin-core-server.nodesversioncompatibility.message.md) | string | | +| [warningNodes](./kibana-plugin-core-server.nodesversioncompatibility.warningnodes.md) | NodeInfo[] | | + diff --git a/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.message.md b/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.message.md new file mode 100644 index 0000000000000..415a7825ee2bf --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.message.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [NodesVersionCompatibility](./kibana-plugin-core-server.nodesversioncompatibility.md) > [message](./kibana-plugin-core-server.nodesversioncompatibility.message.md) + +## NodesVersionCompatibility.message property + +Signature: + +```typescript +message?: string; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.warningnodes.md b/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.warningnodes.md new file mode 100644 index 0000000000000..6c017e9fc800c --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.nodesversioncompatibility.warningnodes.md @@ -0,0 +1,11 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [NodesVersionCompatibility](./kibana-plugin-core-server.nodesversioncompatibility.md) > [warningNodes](./kibana-plugin-core-server.nodesversioncompatibility.warningnodes.md) + +## NodesVersionCompatibility.warningNodes property + +Signature: + +```typescript +warningNodes: NodeInfo[]; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectstatusmeta.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectstatusmeta.md new file mode 100644 index 0000000000000..3a0b23d18632f --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectstatusmeta.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectStatusMeta](./kibana-plugin-core-server.savedobjectstatusmeta.md) + +## SavedObjectStatusMeta interface + +Meta information about the SavedObjectService's status. Available to plugins via [CoreSetup.status](./kibana-plugin-core-server.coresetup.status.md). + +Signature: + +```typescript +export interface SavedObjectStatusMeta +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [migratedIndices](./kibana-plugin-core-server.savedobjectstatusmeta.migratedindices.md) | {
[status: string]: number;
skipped: number;
migrated: number;
} | | + diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectstatusmeta.migratedindices.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectstatusmeta.migratedindices.md new file mode 100644 index 0000000000000..6a29623b2f122 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectstatusmeta.migratedindices.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectStatusMeta](./kibana-plugin-core-server.savedobjectstatusmeta.md) > [migratedIndices](./kibana-plugin-core-server.savedobjectstatusmeta.migratedindices.md) + +## SavedObjectStatusMeta.migratedIndices property + +Signature: + +```typescript +migratedIndices: { + [status: string]: number; + skipped: number; + migrated: number; + }; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.servicestatus.detail.md b/docs/development/core/server/kibana-plugin-core-server.servicestatus.detail.md new file mode 100644 index 0000000000000..fa369aa0bdfbb --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.servicestatus.detail.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ServiceStatus](./kibana-plugin-core-server.servicestatus.md) > [detail](./kibana-plugin-core-server.servicestatus.detail.md) + +## ServiceStatus.detail property + +A more detailed description of the service status. + +Signature: + +```typescript +detail?: string; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.servicestatus.documentationurl.md b/docs/development/core/server/kibana-plugin-core-server.servicestatus.documentationurl.md new file mode 100644 index 0000000000000..5ef8c1251a602 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.servicestatus.documentationurl.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ServiceStatus](./kibana-plugin-core-server.servicestatus.md) > [documentationUrl](./kibana-plugin-core-server.servicestatus.documentationurl.md) + +## ServiceStatus.documentationUrl property + +A URL to open in a new tab about how to resolve or troubleshoot the problem. + +Signature: + +```typescript +documentationUrl?: string; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.servicestatus.level.md b/docs/development/core/server/kibana-plugin-core-server.servicestatus.level.md new file mode 100644 index 0000000000000..551c10c9bff82 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.servicestatus.level.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ServiceStatus](./kibana-plugin-core-server.servicestatus.md) > [level](./kibana-plugin-core-server.servicestatus.level.md) + +## ServiceStatus.level property + +The current availability level of the service. + +Signature: + +```typescript +level: ServiceStatusLevel; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.servicestatus.md b/docs/development/core/server/kibana-plugin-core-server.servicestatus.md new file mode 100644 index 0000000000000..d35fc951c57ff --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.servicestatus.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ServiceStatus](./kibana-plugin-core-server.servicestatus.md) + +## ServiceStatus interface + +The current status of a service at a point in time. + +Signature: + +```typescript +export interface ServiceStatus | unknown = unknown> +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [detail](./kibana-plugin-core-server.servicestatus.detail.md) | string | A more detailed description of the service status. | +| [documentationUrl](./kibana-plugin-core-server.servicestatus.documentationurl.md) | string | A URL to open in a new tab about how to resolve or troubleshoot the problem. | +| [level](./kibana-plugin-core-server.servicestatus.level.md) | ServiceStatusLevel | The current availability level of the service. | +| [meta](./kibana-plugin-core-server.servicestatus.meta.md) | Meta | Any JSON-serializable data to be included in the HTTP API response. Useful for providing more fine-grained, machine-readable information about the service status. May include status information for underlying features. | +| [summary](./kibana-plugin-core-server.servicestatus.summary.md) | string | A high-level summary of the service status. | + diff --git a/docs/development/core/server/kibana-plugin-core-server.servicestatus.meta.md b/docs/development/core/server/kibana-plugin-core-server.servicestatus.meta.md new file mode 100644 index 0000000000000..a48994daa5a4e --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.servicestatus.meta.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ServiceStatus](./kibana-plugin-core-server.servicestatus.md) > [meta](./kibana-plugin-core-server.servicestatus.meta.md) + +## ServiceStatus.meta property + +Any JSON-serializable data to be included in the HTTP API response. Useful for providing more fine-grained, machine-readable information about the service status. May include status information for underlying features. + +Signature: + +```typescript +meta?: Meta; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.servicestatus.summary.md b/docs/development/core/server/kibana-plugin-core-server.servicestatus.summary.md new file mode 100644 index 0000000000000..db90afd6f74a6 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.servicestatus.summary.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ServiceStatus](./kibana-plugin-core-server.servicestatus.md) > [summary](./kibana-plugin-core-server.servicestatus.summary.md) + +## ServiceStatus.summary property + +A high-level summary of the service status. + +Signature: + +```typescript +summary: string; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.servicestatuslevel.md b/docs/development/core/server/kibana-plugin-core-server.servicestatuslevel.md new file mode 100644 index 0000000000000..5f995ff5e13e3 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.servicestatuslevel.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ServiceStatusLevel](./kibana-plugin-core-server.servicestatuslevel.md) + +## ServiceStatusLevel type + +A convenience type that represents the union of each value in [ServiceStatusLevels](./kibana-plugin-core-server.servicestatuslevels.md). + +Signature: + +```typescript +export declare type ServiceStatusLevel = typeof ServiceStatusLevels[keyof typeof ServiceStatusLevels]; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.servicestatuslevels.md b/docs/development/core/server/kibana-plugin-core-server.servicestatuslevels.md new file mode 100644 index 0000000000000..a66cec78c736b --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.servicestatuslevels.md @@ -0,0 +1,37 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ServiceStatusLevels](./kibana-plugin-core-server.servicestatuslevels.md) + +## ServiceStatusLevels variable + +The current "level" of availability of a service. + +Signature: + +```typescript +ServiceStatusLevels: Readonly<{ + available: Readonly<{ + toString: () => "available"; + valueOf: () => 0; + }>; + degraded: Readonly<{ + toString: () => "degraded"; + valueOf: () => 1; + }>; + unavailable: Readonly<{ + toString: () => "unavailable"; + valueOf: () => 2; + }>; + critical: Readonly<{ + toString: () => "critical"; + valueOf: () => 3; + }>; +}> +``` + +## Remarks + +The values implement `valueOf` to allow for easy comparisons between status levels with <, >, etc. Higher values represent higher severities. Note that the default `Array.prototype.sort` implementation does not correctly sort these values. + +A snapshot serializer is available in `src/core/server/test_utils` to ease testing of these values with Jest. + diff --git a/docs/development/core/server/kibana-plugin-core-server.statusservicesetup.core_.md b/docs/development/core/server/kibana-plugin-core-server.statusservicesetup.core_.md new file mode 100644 index 0000000000000..6662e68b44d36 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.statusservicesetup.core_.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [StatusServiceSetup](./kibana-plugin-core-server.statusservicesetup.md) > [core$](./kibana-plugin-core-server.statusservicesetup.core_.md) + +## StatusServiceSetup.core$ property + +Current status for all Core services. + +Signature: + +```typescript +core$: Observable; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.statusservicesetup.md b/docs/development/core/server/kibana-plugin-core-server.statusservicesetup.md new file mode 100644 index 0000000000000..0551a217520ad --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.statusservicesetup.md @@ -0,0 +1,20 @@ + + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [StatusServiceSetup](./kibana-plugin-core-server.statusservicesetup.md) + +## StatusServiceSetup interface + +API for accessing status of Core and this plugin's dependencies as well as for customizing this plugin's status. + +Signature: + +```typescript +export interface StatusServiceSetup +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [core$](./kibana-plugin-core-server.statusservicesetup.core_.md) | Observable<CoreStatus> | Current status for all Core services. | + diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.createsearchsource.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.createsearchsource.md new file mode 100644 index 0000000000000..5c5aa348eecdf --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.createsearchsource.md @@ -0,0 +1,15 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [createSearchSource](./kibana-plugin-plugins-data-public.createsearchsource.md) + +## createSearchSource variable + +Deserializes a json string and a set of referenced objects to a `SearchSource` instance. Use this method to re-create the search source serialized using `searchSource.serialize`. + +This function is a factory function that returns the actual utility when calling it with the required service dependency (index patterns contract). A pre-wired version is also exposed in the start contract of the data plugin as part of the search service + +Signature: + +```typescript +createSearchSource: (indexPatterns: Pick) => (searchSourceJson: string, references: SavedObjectReference[]) => Promise +``` 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 6964c070097c5..fc0dab94a0f65 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 @@ -102,6 +102,7 @@ | [castEsToKbnFieldTypeName](./kibana-plugin-plugins-data-public.castestokbnfieldtypename.md) | Get the KbnFieldType name for an esType string | | [connectToQueryState](./kibana-plugin-plugins-data-public.connecttoquerystate.md) | Helper to setup two-way syncing of global data and a state container | | [createSavedQueryService](./kibana-plugin-plugins-data-public.createsavedqueryservice.md) | | +| [createSearchSource](./kibana-plugin-plugins-data-public.createsearchsource.md) | Deserializes a json string and a set of referenced objects to a SearchSource instance. Use this method to re-create the search source serialized using searchSource.serialize.This function is a factory function that returns the actual utility when calling it with the required service dependency (index patterns contract). A pre-wired version is also exposed in the start contract of the data plugin as part of the search service | | [ES\_SEARCH\_STRATEGY](./kibana-plugin-plugins-data-public.es_search_strategy.md) | | | [esFilters](./kibana-plugin-plugins-data-public.esfilters.md) | | | [esKuery](./kibana-plugin-plugins-data-public.eskuery.md) | | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md index 8e1dbb6e2671d..5f2fc809a5590 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.md @@ -38,6 +38,7 @@ export declare class SearchSource | [getParent()](./kibana-plugin-plugins-data-public.searchsource.getparent.md) | | Get the parent of this SearchSource {undefined\|searchSource} | | [getSearchRequestBody()](./kibana-plugin-plugins-data-public.searchsource.getsearchrequestbody.md) | | | | [onRequestStart(handler)](./kibana-plugin-plugins-data-public.searchsource.onrequeststart.md) | | Add a handler that will be notified whenever requests start | +| [serialize()](./kibana-plugin-plugins-data-public.searchsource.serialize.md) | | Serializes the instance to a JSON string and a set of referenced objects. Use this method to get a representation of the search source which can be stored in a saved object.The references returned by this function can be mixed with other references in the same object, however make sure there are no name-collisions. The references will be named kibanaSavedObjectMeta.searchSourceJSON.index and kibanaSavedObjectMeta.searchSourceJSON.filter[<number>].meta.index.Using createSearchSource, the instance can be re-created. | | [setField(field, value)](./kibana-plugin-plugins-data-public.searchsource.setfield.md) | | | | [setFields(newFields)](./kibana-plugin-plugins-data-public.searchsource.setfields.md) | | | | [setParent(parent, options)](./kibana-plugin-plugins-data-public.searchsource.setparent.md) | | Set a searchSource that this source should inherit from | diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.serialize.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.serialize.md new file mode 100644 index 0000000000000..52d25dec01dfd --- /dev/null +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.searchsource.serialize.md @@ -0,0 +1,27 @@ + + +[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [SearchSource](./kibana-plugin-plugins-data-public.searchsource.md) > [serialize](./kibana-plugin-plugins-data-public.searchsource.serialize.md) + +## SearchSource.serialize() method + +Serializes the instance to a JSON string and a set of referenced objects. Use this method to get a representation of the search source which can be stored in a saved object. + +The references returned by this function can be mixed with other references in the same object, however make sure there are no name-collisions. The references will be named `kibanaSavedObjectMeta.searchSourceJSON.index` and `kibanaSavedObjectMeta.searchSourceJSON.filter[].meta.index`. + +Using `createSearchSource`, the instance can be re-created. + +Signature: + +```typescript +serialize(): { + searchSourceJSON: string; + references: SavedObjectReference[]; + }; +``` +Returns: + +`{ + searchSourceJSON: string; + references: SavedObjectReference[]; + }` + diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md index 259d725b3bf0d..e756eb9b72905 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md @@ -23,7 +23,6 @@ | Function | Description | | --- | --- | | [getDefaultSearchParams(config)](./kibana-plugin-plugins-data-server.getdefaultsearchparams.md) | | -| [getTotalLoaded({ total, failed, successful })](./kibana-plugin-plugins-data-server.gettotalloaded.md) | | | [parseInterval(interval)](./kibana-plugin-plugins-data-server.parseinterval.md) | | | [plugin(initializerContext)](./kibana-plugin-plugins-data-server.plugin.md) | Static code to be shared externally | | [shouldReadFieldFromDocValues(aggregatable, esType)](./kibana-plugin-plugins-data-server.shouldreadfieldfromdocvalues.md) | | diff --git a/docs/user/security/authorization/kibana-privileges.asciidoc b/docs/user/security/authorization/kibana-privileges.asciidoc index 6a0b7cefab018..21fcb9bdccb87 100644 --- a/docs/user/security/authorization/kibana-privileges.asciidoc +++ b/docs/user/security/authorization/kibana-privileges.asciidoc @@ -43,6 +43,10 @@ Assigning a feature privilege grants access to a specific feature. `all`:: Grants full read-write access. `read`:: Grants read-only access. +===== Sub-feature privileges +Some features allow for finer access control than the `all` and `read` privileges. +This additional level of control is available in the Gold subscription level and higher. + ===== Assigning feature privileges From the role management screen: @@ -62,7 +66,8 @@ PUT /api/security/role/my_kibana_role { "base": [], "feature": { - "dashboard": ["all"] + "visualize": ["all"], + "dashboard": ["read", "url_create"] }, "spaces": ["marketing"] } diff --git a/docs/user/security/images/assign_feature_privilege.png b/docs/user/security/images/assign_feature_privilege.png index e7d000d4554ad..26fbbf20b39ad 100644 Binary files a/docs/user/security/images/assign_feature_privilege.png and b/docs/user/security/images/assign_feature_privilege.png differ diff --git a/package.json b/package.json index 4c5db5321c282..bd0fec3a5c116 100644 --- a/package.json +++ b/package.json @@ -376,7 +376,7 @@ "@types/recompose": "^0.30.6", "@types/redux-actions": "^2.6.1", "@types/request": "^2.48.2", - "@types/selenium-webdriver": "^4.0.5", + "@types/selenium-webdriver": "4.0.9", "@types/semver": "^5.5.0", "@types/sinon": "^7.0.13", "@types/strip-ansi": "^3.0.0", @@ -462,6 +462,7 @@ "load-grunt-config": "^3.0.1", "mocha": "^7.1.1", "mock-http-server": "1.3.0", + "ms-chromium-edge-driver": "^0.2.0", "multistream": "^2.1.1", "murmurhash3js": "3.0.1", "mutation-observer": "^1.0.3", @@ -480,7 +481,7 @@ "react-textarea-autosize": "^7.1.2", "regenerate": "^1.4.0", "sass-lint": "^1.12.1", - "selenium-webdriver": "^4.0.0-alpha.5", + "selenium-webdriver": "^4.0.0-alpha.7", "simple-git": "1.116.0", "simplebar-react": "^2.1.0", "sinon": "^7.4.2", diff --git a/packages/kbn-optimizer/package.json b/packages/kbn-optimizer/package.json index b648004760d7c..b3e5a8c518682 100644 --- a/packages/kbn-optimizer/package.json +++ b/packages/kbn-optimizer/package.json @@ -32,6 +32,7 @@ "json-stable-stringify": "^1.0.1", "loader-utils": "^1.2.3", "node-sass": "^4.13.0", + "normalize-path": "^3.0.0", "postcss-loader": "^3.0.0", "raw-loader": "^3.1.0", "resolve-url-loader": "^3.1.1", diff --git a/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap b/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap index d52d89eebe2f1..4b4bb1282d939 100644 --- a/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap +++ b/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap @@ -57,6 +57,6 @@ OptimizerConfig { } `; -exports[`builds expected bundles, saves bundle counts to metadata: bar bundle 1`] = `"var __kbnBundles__=typeof __kbnBundles__===\\"object\\"?__kbnBundles__:{};__kbnBundles__[\\"plugin/bar\\"]=function(modules){function webpackJsonpCallback(data){var chunkIds=data[0];var moreModules=data[1];var moduleId,chunkId,i=0,resolves=[];for(;i bundle.id === p.id)) { + return; + } + + // ignore requests that don't include a /data/public, /kibana_react/public, or + // /kibana_utils/public segment as a cheap way to avoid doing path resolution + // for paths that couldn't possibly resolve to what we're looking for + const reqToStaticBundle = STATIC_BUNDLE_PLUGINS.some(p => + request.includes(`/${p.dirname}/public`) + ); + if (!reqToStaticBundle) { + return; + } + + // determine the most acurate resolution string we can without running full resolution + const rootRelative = normalizePath( + Path.relative(bundle.sourceRoot, Path.resolve(context, request)) + ); + for (const { id, dirname } of STATIC_BUNDLE_PLUGINS) { + if (rootRelative === `src/plugins/${dirname}/public`) { + return `__kbnBundles__['plugin/${id}']`; + } + } + + // import doesn't match a root public import + return undefined; +} + export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) { const commonConfig: webpack.Configuration = { node: { fs: 'empty' }, @@ -63,7 +117,6 @@ export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) { // When the entry point is loaded, assign it's exported `plugin` // value to a key on the global `__kbnBundles__` object. library: ['__kbnBundles__', `plugin/${bundle.id}`], - libraryExport: 'plugin', } : {}), }, @@ -72,9 +125,16 @@ export function getWebpackConfig(bundle: Bundle, worker: WorkerConfig) { noEmitOnErrors: true, }, - externals: { - ...UiSharedDeps.externals, - }, + externals: [ + UiSharedDeps.externals, + function(context, request, cb) { + try { + cb(undefined, dynamicExternals(bundle, context, request)); + } catch (error) { + cb(error, undefined); + } + }, + ], plugins: [new CleanWebpackPlugin(), new DisallowedSyntaxPlugin()], diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 0cc1ad6326671..7a858deff41d3 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -79260,7 +79260,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _build_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(705); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return _build_production_projects__WEBPACK_IMPORTED_MODULE_0__["buildProductionProjects"]; }); -/* harmony import */ var _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(928); +/* harmony import */ var _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(923); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "prepareExternalProjectDependencies", function() { return _prepare_project_dependencies__WEBPACK_IMPORTED_MODULE_1__["prepareExternalProjectDependencies"]; }); /* @@ -79445,9 +79445,9 @@ const pAll = __webpack_require__(707); const arrify = __webpack_require__(709); const globby = __webpack_require__(710); const isGlob = __webpack_require__(604); -const cpFile = __webpack_require__(913); -const junk = __webpack_require__(925); -const CpyError = __webpack_require__(926); +const cpFile = __webpack_require__(908); +const junk = __webpack_require__(920); +const CpyError = __webpack_require__(921); const defaultOptions = { ignoreJunk: true @@ -79697,8 +79697,8 @@ const fs = __webpack_require__(23); const arrayUnion = __webpack_require__(711); const glob = __webpack_require__(713); const fastGlob = __webpack_require__(718); -const dirGlob = __webpack_require__(906); -const gitignore = __webpack_require__(909); +const dirGlob = __webpack_require__(901); +const gitignore = __webpack_require__(904); const DEFAULT_FILTER = () => false; @@ -81531,11 +81531,11 @@ module.exports.generateTasks = pkg.generateTasks; Object.defineProperty(exports, "__esModule", { value: true }); var optionsManager = __webpack_require__(720); var taskManager = __webpack_require__(721); -var reader_async_1 = __webpack_require__(877); -var reader_stream_1 = __webpack_require__(901); -var reader_sync_1 = __webpack_require__(902); -var arrayUtils = __webpack_require__(904); -var streamUtils = __webpack_require__(905); +var reader_async_1 = __webpack_require__(872); +var reader_stream_1 = __webpack_require__(896); +var reader_sync_1 = __webpack_require__(897); +var arrayUtils = __webpack_require__(899); +var streamUtils = __webpack_require__(900); /** * Synchronous API. */ @@ -82175,9 +82175,9 @@ var extend = __webpack_require__(838); */ var compilers = __webpack_require__(841); -var parsers = __webpack_require__(873); -var cache = __webpack_require__(874); -var utils = __webpack_require__(875); +var parsers = __webpack_require__(868); +var cache = __webpack_require__(869); +var utils = __webpack_require__(870); var MAX_LENGTH = 1024 * 64; /** @@ -100710,9 +100710,9 @@ var toRegex = __webpack_require__(729); */ var compilers = __webpack_require__(858); -var parsers = __webpack_require__(869); -var Extglob = __webpack_require__(872); -var utils = __webpack_require__(871); +var parsers = __webpack_require__(864); +var Extglob = __webpack_require__(867); +var utils = __webpack_require__(866); var MAX_LENGTH = 1024 * 64; /** @@ -101222,7 +101222,7 @@ var parsers = __webpack_require__(862); * Module dependencies */ -var debug = __webpack_require__(864)('expand-brackets'); +var debug = __webpack_require__(801)('expand-brackets'); var extend = __webpack_require__(738); var Snapdragon = __webpack_require__(768); var toRegex = __webpack_require__(729); @@ -101816,839 +101816,12 @@ exports.createRegex = function(pattern, include) { /* 864 */ /***/ (function(module, exports, __webpack_require__) { -/** - * Detect Electron renderer process, which is node, but we should - * treat as a browser. - */ - -if (typeof process !== 'undefined' && process.type === 'renderer') { - module.exports = __webpack_require__(865); -} else { - module.exports = __webpack_require__(868); -} - - -/***/ }), -/* 865 */ -/***/ (function(module, exports, __webpack_require__) { - -/** - * This is the web browser implementation of `debug()`. - * - * Expose `debug()` as the module. - */ - -exports = module.exports = __webpack_require__(866); -exports.log = log; -exports.formatArgs = formatArgs; -exports.save = save; -exports.load = load; -exports.useColors = useColors; -exports.storage = 'undefined' != typeof chrome - && 'undefined' != typeof chrome.storage - ? chrome.storage.local - : localstorage(); - -/** - * Colors. - */ - -exports.colors = [ - 'lightseagreen', - 'forestgreen', - 'goldenrod', - 'dodgerblue', - 'darkorchid', - 'crimson' -]; - -/** - * Currently only WebKit-based Web Inspectors, Firefox >= v31, - * and the Firebug extension (any Firefox version) are known - * to support "%c" CSS customizations. - * - * TODO: add a `localStorage` variable to explicitly enable/disable colors - */ - -function useColors() { - // NB: In an Electron preload script, document will be defined but not fully - // initialized. Since we know we're in Chrome, we'll just detect this case - // explicitly - if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') { - return true; - } - - // is webkit? http://stackoverflow.com/a/16459606/376773 - // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632 - return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) || - // is firebug? http://stackoverflow.com/a/398120/376773 - (typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) || - // is firefox >= v31? - // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages - (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) || - // double check webkit in userAgent just in case we are in a worker - (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)); -} - -/** - * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default. - */ - -exports.formatters.j = function(v) { - try { - return JSON.stringify(v); - } catch (err) { - return '[UnexpectedJSONParseError]: ' + err.message; - } -}; - - -/** - * Colorize log arguments if enabled. - * - * @api public - */ - -function formatArgs(args) { - var useColors = this.useColors; - - args[0] = (useColors ? '%c' : '') - + this.namespace - + (useColors ? ' %c' : ' ') - + args[0] - + (useColors ? '%c ' : ' ') - + '+' + exports.humanize(this.diff); - - if (!useColors) return; - - var c = 'color: ' + this.color; - args.splice(1, 0, c, 'color: inherit') - - // the final "%c" is somewhat tricky, because there could be other - // arguments passed either before or after the %c, so we need to - // figure out the correct index to insert the CSS into - var index = 0; - var lastC = 0; - args[0].replace(/%[a-zA-Z%]/g, function(match) { - if ('%%' === match) return; - index++; - if ('%c' === match) { - // we only are interested in the *last* %c - // (the user may have provided their own) - lastC = index; - } - }); - - args.splice(lastC, 0, c); -} - -/** - * Invokes `console.log()` when available. - * No-op when `console.log` is not a "function". - * - * @api public - */ - -function log() { - // this hackery is required for IE8/9, where - // the `console.log` function doesn't have 'apply' - return 'object' === typeof console - && console.log - && Function.prototype.apply.call(console.log, console, arguments); -} - -/** - * Save `namespaces`. - * - * @param {String} namespaces - * @api private - */ - -function save(namespaces) { - try { - if (null == namespaces) { - exports.storage.removeItem('debug'); - } else { - exports.storage.debug = namespaces; - } - } catch(e) {} -} - -/** - * Load `namespaces`. - * - * @return {String} returns the previously persisted debug modes - * @api private - */ - -function load() { - var r; - try { - r = exports.storage.debug; - } catch(e) {} - - // If debug isn't set in LS, and we're in Electron, try to load $DEBUG - if (!r && typeof process !== 'undefined' && 'env' in process) { - r = process.env.DEBUG; - } - - return r; -} - -/** - * Enable namespaces listed in `localStorage.debug` initially. - */ - -exports.enable(load()); - -/** - * Localstorage attempts to return the localstorage. - * - * This is necessary because safari throws - * when a user disables cookies/localstorage - * and you attempt to access it. - * - * @return {LocalStorage} - * @api private - */ - -function localstorage() { - try { - return window.localStorage; - } catch (e) {} -} - - -/***/ }), -/* 866 */ -/***/ (function(module, exports, __webpack_require__) { - - -/** - * This is the common logic for both the Node.js and web browser - * implementations of `debug()`. - * - * Expose `debug()` as the module. - */ - -exports = module.exports = createDebug.debug = createDebug['default'] = createDebug; -exports.coerce = coerce; -exports.disable = disable; -exports.enable = enable; -exports.enabled = enabled; -exports.humanize = __webpack_require__(867); - -/** - * The currently active debug mode names, and names to skip. - */ - -exports.names = []; -exports.skips = []; - -/** - * Map of special "%n" handling functions, for the debug "format" argument. - * - * Valid key names are a single, lower or upper-case letter, i.e. "n" and "N". - */ - -exports.formatters = {}; - -/** - * Previous log timestamp. - */ - -var prevTime; - -/** - * Select a color. - * @param {String} namespace - * @return {Number} - * @api private - */ - -function selectColor(namespace) { - var hash = 0, i; - - for (i in namespace) { - hash = ((hash << 5) - hash) + namespace.charCodeAt(i); - hash |= 0; // Convert to 32bit integer - } - - return exports.colors[Math.abs(hash) % exports.colors.length]; -} - -/** - * Create a debugger with the given `namespace`. - * - * @param {String} namespace - * @return {Function} - * @api public - */ - -function createDebug(namespace) { - - function debug() { - // disabled? - if (!debug.enabled) return; - - var self = debug; - - // set `diff` timestamp - var curr = +new Date(); - var ms = curr - (prevTime || curr); - self.diff = ms; - self.prev = prevTime; - self.curr = curr; - prevTime = curr; - - // turn the `arguments` into a proper Array - var args = new Array(arguments.length); - for (var i = 0; i < args.length; i++) { - args[i] = arguments[i]; - } - - args[0] = exports.coerce(args[0]); - - if ('string' !== typeof args[0]) { - // anything else let's inspect with %O - args.unshift('%O'); - } - - // apply any `formatters` transformations - var index = 0; - args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) { - // if we encounter an escaped % then don't increase the array index - if (match === '%%') return match; - index++; - var formatter = exports.formatters[format]; - if ('function' === typeof formatter) { - var val = args[index]; - match = formatter.call(self, val); - - // now we need to remove `args[index]` since it's inlined in the `format` - args.splice(index, 1); - index--; - } - return match; - }); - - // apply env-specific formatting (colors, etc.) - exports.formatArgs.call(self, args); - - var logFn = debug.log || exports.log || console.log.bind(console); - logFn.apply(self, args); - } - - debug.namespace = namespace; - debug.enabled = exports.enabled(namespace); - debug.useColors = exports.useColors(); - debug.color = selectColor(namespace); - - // env-specific initialization logic for debug instances - if ('function' === typeof exports.init) { - exports.init(debug); - } - - return debug; -} - -/** - * Enables a debug mode by namespaces. This can include modes - * separated by a colon and wildcards. - * - * @param {String} namespaces - * @api public - */ - -function enable(namespaces) { - exports.save(namespaces); - - exports.names = []; - exports.skips = []; - - var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/); - var len = split.length; - - for (var i = 0; i < len; i++) { - if (!split[i]) continue; // ignore empty strings - namespaces = split[i].replace(/\*/g, '.*?'); - if (namespaces[0] === '-') { - exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$')); - } else { - exports.names.push(new RegExp('^' + namespaces + '$')); - } - } -} - -/** - * Disable debug output. - * - * @api public - */ - -function disable() { - exports.enable(''); -} - -/** - * Returns true if the given mode name is enabled, false otherwise. - * - * @param {String} name - * @return {Boolean} - * @api public - */ - -function enabled(name) { - var i, len; - for (i = 0, len = exports.skips.length; i < len; i++) { - if (exports.skips[i].test(name)) { - return false; - } - } - for (i = 0, len = exports.names.length; i < len; i++) { - if (exports.names[i].test(name)) { - return true; - } - } - return false; -} - -/** - * Coerce `val`. - * - * @param {Mixed} val - * @return {Mixed} - * @api private - */ - -function coerce(val) { - if (val instanceof Error) return val.stack || val.message; - return val; -} - - -/***/ }), -/* 867 */ -/***/ (function(module, exports) { - -/** - * Helpers. - */ - -var s = 1000; -var m = s * 60; -var h = m * 60; -var d = h * 24; -var y = d * 365.25; - -/** - * Parse or format the given `val`. - * - * Options: - * - * - `long` verbose formatting [false] - * - * @param {String|Number} val - * @param {Object} [options] - * @throws {Error} throw an error if val is not a non-empty string or a number - * @return {String|Number} - * @api public - */ - -module.exports = function(val, options) { - options = options || {}; - var type = typeof val; - if (type === 'string' && val.length > 0) { - return parse(val); - } else if (type === 'number' && isNaN(val) === false) { - return options.long ? fmtLong(val) : fmtShort(val); - } - throw new Error( - 'val is not a non-empty string or a valid number. val=' + - JSON.stringify(val) - ); -}; - -/** - * Parse the given `str` and return milliseconds. - * - * @param {String} str - * @return {Number} - * @api private - */ - -function parse(str) { - str = String(str); - if (str.length > 100) { - return; - } - var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec( - str - ); - if (!match) { - return; - } - var n = parseFloat(match[1]); - var type = (match[2] || 'ms').toLowerCase(); - switch (type) { - case 'years': - case 'year': - case 'yrs': - case 'yr': - case 'y': - return n * y; - case 'days': - case 'day': - case 'd': - return n * d; - case 'hours': - case 'hour': - case 'hrs': - case 'hr': - case 'h': - return n * h; - case 'minutes': - case 'minute': - case 'mins': - case 'min': - case 'm': - return n * m; - case 'seconds': - case 'second': - case 'secs': - case 'sec': - case 's': - return n * s; - case 'milliseconds': - case 'millisecond': - case 'msecs': - case 'msec': - case 'ms': - return n; - default: - return undefined; - } -} - -/** - * Short format for `ms`. - * - * @param {Number} ms - * @return {String} - * @api private - */ - -function fmtShort(ms) { - if (ms >= d) { - return Math.round(ms / d) + 'd'; - } - if (ms >= h) { - return Math.round(ms / h) + 'h'; - } - if (ms >= m) { - return Math.round(ms / m) + 'm'; - } - if (ms >= s) { - return Math.round(ms / s) + 's'; - } - return ms + 'ms'; -} - -/** - * Long format for `ms`. - * - * @param {Number} ms - * @return {String} - * @api private - */ - -function fmtLong(ms) { - return plural(ms, d, 'day') || - plural(ms, h, 'hour') || - plural(ms, m, 'minute') || - plural(ms, s, 'second') || - ms + ' ms'; -} - -/** - * Pluralization helper. - */ - -function plural(ms, n, name) { - if (ms < n) { - return; - } - if (ms < n * 1.5) { - return Math.floor(ms / n) + ' ' + name; - } - return Math.ceil(ms / n) + ' ' + name + 's'; -} - - -/***/ }), -/* 868 */ -/***/ (function(module, exports, __webpack_require__) { - -/** - * Module dependencies. - */ - -var tty = __webpack_require__(478); -var util = __webpack_require__(29); - -/** - * This is the Node.js implementation of `debug()`. - * - * Expose `debug()` as the module. - */ - -exports = module.exports = __webpack_require__(866); -exports.init = init; -exports.log = log; -exports.formatArgs = formatArgs; -exports.save = save; -exports.load = load; -exports.useColors = useColors; - -/** - * Colors. - */ - -exports.colors = [6, 2, 3, 4, 5, 1]; - -/** - * Build up the default `inspectOpts` object from the environment variables. - * - * $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js - */ - -exports.inspectOpts = Object.keys(process.env).filter(function (key) { - return /^debug_/i.test(key); -}).reduce(function (obj, key) { - // camel-case - var prop = key - .substring(6) - .toLowerCase() - .replace(/_([a-z])/g, function (_, k) { return k.toUpperCase() }); - - // coerce string value into JS value - var val = process.env[key]; - if (/^(yes|on|true|enabled)$/i.test(val)) val = true; - else if (/^(no|off|false|disabled)$/i.test(val)) val = false; - else if (val === 'null') val = null; - else val = Number(val); - - obj[prop] = val; - return obj; -}, {}); - -/** - * The file descriptor to write the `debug()` calls to. - * Set the `DEBUG_FD` env variable to override with another value. i.e.: - * - * $ DEBUG_FD=3 node script.js 3>debug.log - */ - -var fd = parseInt(process.env.DEBUG_FD, 10) || 2; - -if (1 !== fd && 2 !== fd) { - util.deprecate(function(){}, 'except for stderr(2) and stdout(1), any other usage of DEBUG_FD is deprecated. Override debug.log if you want to use a different log function (https://git.io/debug_fd)')() -} - -var stream = 1 === fd ? process.stdout : - 2 === fd ? process.stderr : - createWritableStdioStream(fd); - -/** - * Is stdout a TTY? Colored output is enabled when `true`. - */ - -function useColors() { - return 'colors' in exports.inspectOpts - ? Boolean(exports.inspectOpts.colors) - : tty.isatty(fd); -} - -/** - * Map %o to `util.inspect()`, all on a single line. - */ - -exports.formatters.o = function(v) { - this.inspectOpts.colors = this.useColors; - return util.inspect(v, this.inspectOpts) - .split('\n').map(function(str) { - return str.trim() - }).join(' '); -}; - -/** - * Map %o to `util.inspect()`, allowing multiple lines if needed. - */ - -exports.formatters.O = function(v) { - this.inspectOpts.colors = this.useColors; - return util.inspect(v, this.inspectOpts); -}; - -/** - * Adds ANSI color escape codes if enabled. - * - * @api public - */ - -function formatArgs(args) { - var name = this.namespace; - var useColors = this.useColors; - - if (useColors) { - var c = this.color; - var prefix = ' \u001b[3' + c + ';1m' + name + ' ' + '\u001b[0m'; - - args[0] = prefix + args[0].split('\n').join('\n' + prefix); - args.push('\u001b[3' + c + 'm+' + exports.humanize(this.diff) + '\u001b[0m'); - } else { - args[0] = new Date().toUTCString() - + ' ' + name + ' ' + args[0]; - } -} - -/** - * Invokes `util.format()` with the specified arguments and writes to `stream`. - */ - -function log() { - return stream.write(util.format.apply(util, arguments) + '\n'); -} - -/** - * Save `namespaces`. - * - * @param {String} namespaces - * @api private - */ - -function save(namespaces) { - if (null == namespaces) { - // If you set a process.env field to null or undefined, it gets cast to the - // string 'null' or 'undefined'. Just delete instead. - delete process.env.DEBUG; - } else { - process.env.DEBUG = namespaces; - } -} - -/** - * Load `namespaces`. - * - * @return {String} returns the previously persisted debug modes - * @api private - */ - -function load() { - return process.env.DEBUG; -} - -/** - * Copied from `node/src/node.js`. - * - * XXX: It's lame that node doesn't expose this API out-of-the-box. It also - * relies on the undocumented `tty_wrap.guessHandleType()` which is also lame. - */ - -function createWritableStdioStream (fd) { - var stream; - var tty_wrap = process.binding('tty_wrap'); - - // Note stream._type is used for test-module-load-list.js - - switch (tty_wrap.guessHandleType(fd)) { - case 'TTY': - stream = new tty.WriteStream(fd); - stream._type = 'tty'; - - // Hack to have stream not keep the event loop alive. - // See https://github.com/joyent/node/issues/1726 - if (stream._handle && stream._handle.unref) { - stream._handle.unref(); - } - break; - - case 'FILE': - var fs = __webpack_require__(23); - stream = new fs.SyncWriteStream(fd, { autoClose: false }); - stream._type = 'fs'; - break; - - case 'PIPE': - case 'TCP': - var net = __webpack_require__(806); - stream = new net.Socket({ - fd: fd, - readable: false, - writable: true - }); - - // FIXME Should probably have an option in net.Socket to create a - // stream from an existing fd which is writable only. But for now - // we'll just add this hack and set the `readable` member to false. - // Test: ./node test/fixtures/echo.js < /etc/passwd - stream.readable = false; - stream.read = null; - stream._type = 'pipe'; - - // FIXME Hack to have stream not keep the event loop alive. - // See https://github.com/joyent/node/issues/1726 - if (stream._handle && stream._handle.unref) { - stream._handle.unref(); - } - break; - - default: - // Probably an error on in uv_guess_handle() - throw new Error('Implement me. Unknown stream file type!'); - } - - // For supporting legacy API we put the FD here. - stream.fd = fd; - - stream._isStdio = true; - - return stream; -} - -/** - * Init logic for `debug` instances. - * - * Create a new `inspectOpts` object in case `useColors` is set - * differently for a particular `debug` instance. - */ - -function init (debug) { - debug.inspectOpts = {}; - - var keys = Object.keys(exports.inspectOpts); - for (var i = 0; i < keys.length; i++) { - debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]]; - } -} - -/** - * Enable namespaces listed in `process.env.DEBUG` initially. - */ - -exports.enable(load()); - - -/***/ }), -/* 869 */ -/***/ (function(module, exports, __webpack_require__) { - "use strict"; var brackets = __webpack_require__(859); -var define = __webpack_require__(870); -var utils = __webpack_require__(871); +var define = __webpack_require__(865); +var utils = __webpack_require__(866); /** * Characters to use in text regex (we want to "not" match @@ -102803,7 +101976,7 @@ module.exports = parsers; /***/ }), -/* 870 */ +/* 865 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -102841,7 +102014,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 871 */ +/* 866 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -102917,7 +102090,7 @@ utils.createRegex = function(str) { /***/ }), -/* 872 */ +/* 867 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -102928,7 +102101,7 @@ utils.createRegex = function(str) { */ var Snapdragon = __webpack_require__(768); -var define = __webpack_require__(870); +var define = __webpack_require__(865); var extend = __webpack_require__(738); /** @@ -102936,7 +102109,7 @@ var extend = __webpack_require__(738); */ var compilers = __webpack_require__(858); -var parsers = __webpack_require__(869); +var parsers = __webpack_require__(864); /** * Customize Snapdragon parser and renderer @@ -103002,7 +102175,7 @@ module.exports = Extglob; /***/ }), -/* 873 */ +/* 868 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103092,14 +102265,14 @@ function textRegex(pattern) { /***/ }), -/* 874 */ +/* 869 */ /***/ (function(module, exports, __webpack_require__) { module.exports = new (__webpack_require__(850))(); /***/ }), -/* 875 */ +/* 870 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103117,7 +102290,7 @@ utils.define = __webpack_require__(837); utils.diff = __webpack_require__(854); utils.extend = __webpack_require__(838); utils.pick = __webpack_require__(855); -utils.typeOf = __webpack_require__(876); +utils.typeOf = __webpack_require__(871); utils.unique = __webpack_require__(741); /** @@ -103415,7 +102588,7 @@ utils.unixify = function(options) { /***/ }), -/* 876 */ +/* 871 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -103550,7 +102723,7 @@ function isBuffer(val) { /***/ }), -/* 877 */ +/* 872 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103569,9 +102742,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(878); -var reader_1 = __webpack_require__(891); -var fs_stream_1 = __webpack_require__(895); +var readdir = __webpack_require__(873); +var reader_1 = __webpack_require__(886); +var fs_stream_1 = __webpack_require__(890); var ReaderAsync = /** @class */ (function (_super) { __extends(ReaderAsync, _super); function ReaderAsync() { @@ -103632,15 +102805,15 @@ exports.default = ReaderAsync; /***/ }), -/* 878 */ +/* 873 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const readdirSync = __webpack_require__(879); -const readdirAsync = __webpack_require__(887); -const readdirStream = __webpack_require__(890); +const readdirSync = __webpack_require__(874); +const readdirAsync = __webpack_require__(882); +const readdirStream = __webpack_require__(885); module.exports = exports = readdirAsyncPath; exports.readdir = exports.readdirAsync = exports.async = readdirAsyncPath; @@ -103724,7 +102897,7 @@ function readdirStreamStat (dir, options) { /***/ }), -/* 879 */ +/* 874 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103732,11 +102905,11 @@ function readdirStreamStat (dir, options) { module.exports = readdirSync; -const DirectoryReader = __webpack_require__(880); +const DirectoryReader = __webpack_require__(875); let syncFacade = { - fs: __webpack_require__(885), - forEach: __webpack_require__(886), + fs: __webpack_require__(880), + forEach: __webpack_require__(881), sync: true }; @@ -103765,7 +102938,7 @@ function readdirSync (dir, options, internalOptions) { /***/ }), -/* 880 */ +/* 875 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -103774,9 +102947,9 @@ function readdirSync (dir, options, internalOptions) { const Readable = __webpack_require__(27).Readable; const EventEmitter = __webpack_require__(379).EventEmitter; const path = __webpack_require__(16); -const normalizeOptions = __webpack_require__(881); -const stat = __webpack_require__(883); -const call = __webpack_require__(884); +const normalizeOptions = __webpack_require__(876); +const stat = __webpack_require__(878); +const call = __webpack_require__(879); /** * Asynchronously reads the contents of a directory and streams the results @@ -104152,14 +103325,14 @@ module.exports = DirectoryReader; /***/ }), -/* 881 */ +/* 876 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); -const globToRegExp = __webpack_require__(882); +const globToRegExp = __webpack_require__(877); module.exports = normalizeOptions; @@ -104336,7 +103509,7 @@ function normalizeOptions (options, internalOptions) { /***/ }), -/* 882 */ +/* 877 */ /***/ (function(module, exports) { module.exports = function (glob, opts) { @@ -104473,13 +103646,13 @@ module.exports = function (glob, opts) { /***/ }), -/* 883 */ +/* 878 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const call = __webpack_require__(884); +const call = __webpack_require__(879); module.exports = stat; @@ -104554,7 +103727,7 @@ function symlinkStat (fs, path, lstats, callback) { /***/ }), -/* 884 */ +/* 879 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104615,14 +103788,14 @@ function callOnce (fn) { /***/ }), -/* 885 */ +/* 880 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); -const call = __webpack_require__(884); +const call = __webpack_require__(879); /** * A facade around {@link fs.readdirSync} that allows it to be called @@ -104686,7 +103859,7 @@ exports.lstat = function (path, callback) { /***/ }), -/* 886 */ +/* 881 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104715,7 +103888,7 @@ function syncForEach (array, iterator, done) { /***/ }), -/* 887 */ +/* 882 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104723,12 +103896,12 @@ function syncForEach (array, iterator, done) { module.exports = readdirAsync; -const maybe = __webpack_require__(888); -const DirectoryReader = __webpack_require__(880); +const maybe = __webpack_require__(883); +const DirectoryReader = __webpack_require__(875); let asyncFacade = { fs: __webpack_require__(23), - forEach: __webpack_require__(889), + forEach: __webpack_require__(884), async: true }; @@ -104770,7 +103943,7 @@ function readdirAsync (dir, options, callback, internalOptions) { /***/ }), -/* 888 */ +/* 883 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104797,7 +103970,7 @@ module.exports = function maybe (cb, promise) { /***/ }), -/* 889 */ +/* 884 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104833,7 +104006,7 @@ function asyncForEach (array, iterator, done) { /***/ }), -/* 890 */ +/* 885 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -104841,11 +104014,11 @@ function asyncForEach (array, iterator, done) { module.exports = readdirStream; -const DirectoryReader = __webpack_require__(880); +const DirectoryReader = __webpack_require__(875); let streamFacade = { fs: __webpack_require__(23), - forEach: __webpack_require__(889), + forEach: __webpack_require__(884), async: true }; @@ -104865,16 +104038,16 @@ function readdirStream (dir, options, internalOptions) { /***/ }), -/* 891 */ +/* 886 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(16); -var deep_1 = __webpack_require__(892); -var entry_1 = __webpack_require__(894); -var pathUtil = __webpack_require__(893); +var deep_1 = __webpack_require__(887); +var entry_1 = __webpack_require__(889); +var pathUtil = __webpack_require__(888); var Reader = /** @class */ (function () { function Reader(options) { this.options = options; @@ -104940,13 +104113,13 @@ exports.default = Reader; /***/ }), -/* 892 */ +/* 887 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(893); +var pathUtils = __webpack_require__(888); var patternUtils = __webpack_require__(722); var DeepFilter = /** @class */ (function () { function DeepFilter(options, micromatchOptions) { @@ -105030,7 +104203,7 @@ exports.default = DeepFilter; /***/ }), -/* 893 */ +/* 888 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105061,13 +104234,13 @@ exports.makeAbsolute = makeAbsolute; /***/ }), -/* 894 */ +/* 889 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(893); +var pathUtils = __webpack_require__(888); var patternUtils = __webpack_require__(722); var EntryFilter = /** @class */ (function () { function EntryFilter(options, micromatchOptions) { @@ -105153,7 +104326,7 @@ exports.default = EntryFilter; /***/ }), -/* 895 */ +/* 890 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105173,8 +104346,8 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(27); -var fsStat = __webpack_require__(896); -var fs_1 = __webpack_require__(900); +var fsStat = __webpack_require__(891); +var fs_1 = __webpack_require__(895); var FileSystemStream = /** @class */ (function (_super) { __extends(FileSystemStream, _super); function FileSystemStream() { @@ -105224,14 +104397,14 @@ exports.default = FileSystemStream; /***/ }), -/* 896 */ +/* 891 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const optionsManager = __webpack_require__(897); -const statProvider = __webpack_require__(899); +const optionsManager = __webpack_require__(892); +const statProvider = __webpack_require__(894); /** * Asynchronous API. */ @@ -105262,13 +104435,13 @@ exports.statSync = statSync; /***/ }), -/* 897 */ +/* 892 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsAdapter = __webpack_require__(898); +const fsAdapter = __webpack_require__(893); function prepare(opts) { const options = Object.assign({ fs: fsAdapter.getFileSystemAdapter(opts ? opts.fs : undefined), @@ -105281,7 +104454,7 @@ exports.prepare = prepare; /***/ }), -/* 898 */ +/* 893 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105304,7 +104477,7 @@ exports.getFileSystemAdapter = getFileSystemAdapter; /***/ }), -/* 899 */ +/* 894 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105356,7 +104529,7 @@ exports.isFollowedSymlink = isFollowedSymlink; /***/ }), -/* 900 */ +/* 895 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105387,7 +104560,7 @@ exports.default = FileSystem; /***/ }), -/* 901 */ +/* 896 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105407,9 +104580,9 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(27); -var readdir = __webpack_require__(878); -var reader_1 = __webpack_require__(891); -var fs_stream_1 = __webpack_require__(895); +var readdir = __webpack_require__(873); +var reader_1 = __webpack_require__(886); +var fs_stream_1 = __webpack_require__(890); var TransformStream = /** @class */ (function (_super) { __extends(TransformStream, _super); function TransformStream(reader) { @@ -105477,7 +104650,7 @@ exports.default = ReaderStream; /***/ }), -/* 902 */ +/* 897 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105496,9 +104669,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(878); -var reader_1 = __webpack_require__(891); -var fs_sync_1 = __webpack_require__(903); +var readdir = __webpack_require__(873); +var reader_1 = __webpack_require__(886); +var fs_sync_1 = __webpack_require__(898); var ReaderSync = /** @class */ (function (_super) { __extends(ReaderSync, _super); function ReaderSync() { @@ -105558,7 +104731,7 @@ exports.default = ReaderSync; /***/ }), -/* 903 */ +/* 898 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105577,8 +104750,8 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var fsStat = __webpack_require__(896); -var fs_1 = __webpack_require__(900); +var fsStat = __webpack_require__(891); +var fs_1 = __webpack_require__(895); var FileSystemSync = /** @class */ (function (_super) { __extends(FileSystemSync, _super); function FileSystemSync() { @@ -105624,7 +104797,7 @@ exports.default = FileSystemSync; /***/ }), -/* 904 */ +/* 899 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105640,7 +104813,7 @@ exports.flatten = flatten; /***/ }), -/* 905 */ +/* 900 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105661,13 +104834,13 @@ exports.merge = merge; /***/ }), -/* 906 */ +/* 901 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); -const pathType = __webpack_require__(907); +const pathType = __webpack_require__(902); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -105733,13 +104906,13 @@ module.exports.sync = (input, opts) => { /***/ }), -/* 907 */ +/* 902 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(23); -const pify = __webpack_require__(908); +const pify = __webpack_require__(903); function type(fn, fn2, fp) { if (typeof fp !== 'string') { @@ -105782,7 +104955,7 @@ exports.symlinkSync = typeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 908 */ +/* 903 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105873,7 +105046,7 @@ module.exports = (obj, opts) => { /***/ }), -/* 909 */ +/* 904 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -105881,9 +105054,9 @@ module.exports = (obj, opts) => { const fs = __webpack_require__(23); const path = __webpack_require__(16); const fastGlob = __webpack_require__(718); -const gitIgnore = __webpack_require__(910); -const pify = __webpack_require__(911); -const slash = __webpack_require__(912); +const gitIgnore = __webpack_require__(905); +const pify = __webpack_require__(906); +const slash = __webpack_require__(907); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -105981,7 +105154,7 @@ module.exports.sync = options => { /***/ }), -/* 910 */ +/* 905 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -106450,7 +105623,7 @@ module.exports = options => new IgnoreBase(options) /***/ }), -/* 911 */ +/* 906 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106525,7 +105698,7 @@ module.exports = (input, options) => { /***/ }), -/* 912 */ +/* 907 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -106543,17 +105716,17 @@ module.exports = input => { /***/ }), -/* 913 */ +/* 908 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(16); const {constants: fsConstants} = __webpack_require__(23); -const pEvent = __webpack_require__(914); -const CpFileError = __webpack_require__(917); -const fs = __webpack_require__(921); -const ProgressEmitter = __webpack_require__(924); +const pEvent = __webpack_require__(909); +const CpFileError = __webpack_require__(912); +const fs = __webpack_require__(916); +const ProgressEmitter = __webpack_require__(919); const cpFileAsync = async (source, destination, options, progressEmitter) => { let readError; @@ -106667,12 +105840,12 @@ module.exports.sync = (source, destination, options) => { /***/ }), -/* 914 */ +/* 909 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pTimeout = __webpack_require__(915); +const pTimeout = __webpack_require__(910); const symbolAsyncIterator = Symbol.asyncIterator || '@@asyncIterator'; @@ -106963,12 +106136,12 @@ module.exports.iterator = (emitter, event, options) => { /***/ }), -/* 915 */ +/* 910 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pFinally = __webpack_require__(916); +const pFinally = __webpack_require__(911); class TimeoutError extends Error { constructor(message) { @@ -107014,7 +106187,7 @@ module.exports.TimeoutError = TimeoutError; /***/ }), -/* 916 */ +/* 911 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -107036,12 +106209,12 @@ module.exports = (promise, onFinally) => { /***/ }), -/* 917 */ +/* 912 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(918); +const NestedError = __webpack_require__(913); class CpFileError extends NestedError { constructor(message, nested) { @@ -107055,10 +106228,10 @@ module.exports = CpFileError; /***/ }), -/* 918 */ +/* 913 */ /***/ (function(module, exports, __webpack_require__) { -var inherits = __webpack_require__(919); +var inherits = __webpack_require__(914); var NestedError = function (message, nested) { this.nested = nested; @@ -107109,7 +106282,7 @@ module.exports = NestedError; /***/ }), -/* 919 */ +/* 914 */ /***/ (function(module, exports, __webpack_require__) { try { @@ -107117,12 +106290,12 @@ try { if (typeof util.inherits !== 'function') throw ''; module.exports = util.inherits; } catch (e) { - module.exports = __webpack_require__(920); + module.exports = __webpack_require__(915); } /***/ }), -/* 920 */ +/* 915 */ /***/ (function(module, exports) { if (typeof Object.create === 'function') { @@ -107151,16 +106324,16 @@ if (typeof Object.create === 'function') { /***/ }), -/* 921 */ +/* 916 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const {promisify} = __webpack_require__(29); const fs = __webpack_require__(22); -const makeDir = __webpack_require__(922); -const pEvent = __webpack_require__(914); -const CpFileError = __webpack_require__(917); +const makeDir = __webpack_require__(917); +const pEvent = __webpack_require__(909); +const CpFileError = __webpack_require__(912); const stat = promisify(fs.stat); const lstat = promisify(fs.lstat); @@ -107257,7 +106430,7 @@ exports.copyFileSync = (source, destination, flags) => { /***/ }), -/* 922 */ +/* 917 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -107265,7 +106438,7 @@ exports.copyFileSync = (source, destination, flags) => { const fs = __webpack_require__(23); const path = __webpack_require__(16); const {promisify} = __webpack_require__(29); -const semver = __webpack_require__(923); +const semver = __webpack_require__(918); const defaults = { mode: 0o777 & (~process.umask()), @@ -107414,7 +106587,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 923 */ +/* 918 */ /***/ (function(module, exports) { exports = module.exports = SemVer @@ -109016,7 +108189,7 @@ function coerce (version, options) { /***/ }), -/* 924 */ +/* 919 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -109057,7 +108230,7 @@ module.exports = ProgressEmitter; /***/ }), -/* 925 */ +/* 920 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -109103,12 +108276,12 @@ exports.default = module.exports; /***/ }), -/* 926 */ +/* 921 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(927); +const NestedError = __webpack_require__(922); class CpyError extends NestedError { constructor(message, nested) { @@ -109122,7 +108295,7 @@ module.exports = CpyError; /***/ }), -/* 927 */ +/* 922 */ /***/ (function(module, exports, __webpack_require__) { var inherits = __webpack_require__(29).inherits; @@ -109178,7 +108351,7 @@ module.exports = NestedError; /***/ }), -/* 928 */ +/* 923 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; diff --git a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts index 66f17ab579ec3..f4b91d154cbb8 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/config/schema.ts @@ -136,7 +136,7 @@ export const schema = Joi.object() browser: Joi.object() .keys({ type: Joi.string() - .valid('chrome', 'firefox', 'ie') + .valid('chrome', 'firefox', 'ie', 'msedge') .default('chrome'), logPollingMs: Joi.number().default(100), diff --git a/packages/kbn-ui-shared-deps/polyfills.js b/packages/kbn-ui-shared-deps/polyfills.js index d2305d643e4d2..1f1392b02baff 100644 --- a/packages/kbn-ui-shared-deps/polyfills.js +++ b/packages/kbn-ui-shared-deps/polyfills.js @@ -20,6 +20,13 @@ require('core-js/stable'); require('regenerator-runtime/runtime'); require('custom-event-polyfill'); + +if (typeof window.Event === 'object') { + // IE11 doesn't support unknown event types, required by react-use + // https://github.com/streamich/react-use/issues/73 + window.Event = CustomEvent; +} + require('whatwg-fetch'); require('abortcontroller-polyfill/dist/polyfill-patch-fetch'); require('./vendor/childnode_remove_polyfill'); diff --git a/src/core/public/plugins/plugin_loader.test.ts b/src/core/public/plugins/plugin_loader.test.ts index e5cbffc3e2d94..b4e2c3095f14a 100644 --- a/src/core/public/plugins/plugin_loader.test.ts +++ b/src/core/public/plugins/plugin_loader.test.ts @@ -71,7 +71,7 @@ test('`loadPluginBundles` creates a script tag and loads initializer', async () // Setup a fake initializer as if a plugin bundle had actually been loaded. const fakeInitializer = jest.fn(); - coreWindow.__kbnBundles__['plugin/plugin-a'] = fakeInitializer; + coreWindow.__kbnBundles__['plugin/plugin-a'] = { plugin: fakeInitializer }; // Call the onload callback fakeScriptTag.onload(); await expect(loadPromise).resolves.toEqual(fakeInitializer); diff --git a/src/core/public/plugins/plugin_loader.ts b/src/core/public/plugins/plugin_loader.ts index 63aba0dde2af8..bf7711055e97b 100644 --- a/src/core/public/plugins/plugin_loader.ts +++ b/src/core/public/plugins/plugin_loader.ts @@ -32,7 +32,7 @@ export type UnknownPluginInitializer = PluginInitializer new Promise>( (resolve, reject) => { - const script = document.createElement('script'); const coreWindow = (window as unknown) as CoreWindow; + const exportId = `plugin/${pluginName}`; + + const readPluginExport = () => { + const PluginExport: any = coreWindow.__kbnBundles__[exportId]; + if (typeof PluginExport?.plugin !== 'function') { + reject( + new Error(`Definition of plugin "${pluginName}" should be a function (${bundlePath}).`) + ); + } else { + resolve( + PluginExport.plugin as PluginInitializer + ); + } + }; + if (coreWindow.__kbnBundles__[exportId]) { + readPluginExport(); + return; + } + + const script = document.createElement('script'); // Assumes that all plugin bundles get put into the bundles/plugins subdirectory const bundlePath = addBasePath(`/bundles/plugin/${pluginName}/${pluginName}.plugin.js`); script.setAttribute('src', bundlePath); @@ -89,15 +108,7 @@ export const loadPluginBundle: LoadPluginBundle = < // Wire up resolve and reject script.onload = () => { cleanupTag(); - - const initializer = coreWindow.__kbnBundles__[`plugin/${pluginName}`]; - if (!initializer || typeof initializer !== 'function') { - reject( - new Error(`Definition of plugin "${pluginName}" should be a function (${bundlePath}).`) - ); - } else { - resolve(initializer as PluginInitializer); - } + readPluginExport(); }; script.onerror = () => { diff --git a/src/core/server/elasticsearch/elasticsearch_service.mock.ts b/src/core/server/elasticsearch/elasticsearch_service.mock.ts index 389d98a0818c8..da8846f6dddbb 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.mock.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.mock.ts @@ -26,8 +26,10 @@ import { InternalElasticsearchServiceSetup, ElasticsearchServiceSetup, ElasticsearchServiceStart, + ElasticsearchStatusMeta, } from './types'; import { NodesVersionCompatibility } from './version_check/ensure_es_version'; +import { ServiceStatus, ServiceStatusLevels } from '../status'; const createScopedClusterClientMock = (): jest.Mocked => ({ callAsInternalUser: jest.fn(), @@ -102,6 +104,10 @@ const createInternalSetupContractMock = () => { warningNodes: [], kibanaVersion: '8.0.0', }), + status$: new BehaviorSubject>({ + level: ServiceStatusLevels.available, + summary: 'Elasticsearch is available', + }), legacy: { config$: new BehaviorSubject({} as ElasticsearchConfig), }, diff --git a/src/core/server/elasticsearch/elasticsearch_service.ts b/src/core/server/elasticsearch/elasticsearch_service.ts index b92a6edf778ed..684f6e15caff9 100644 --- a/src/core/server/elasticsearch/elasticsearch_service.ts +++ b/src/core/server/elasticsearch/elasticsearch_service.ts @@ -40,6 +40,7 @@ import { InternalHttpServiceSetup, GetAuthHeaders } from '../http/'; import { InternalElasticsearchServiceSetup, ElasticsearchServiceStart } from './types'; import { CallAPIOptions } from './api_types'; import { pollEsNodesVersion } from './version_check/ensure_es_version'; +import { calculateStatus$ } from './status'; /** @internal */ interface CoreClusterClients { @@ -186,6 +187,7 @@ export class ElasticsearchService adminClient: this.adminClient, dataClient, createClient: this.createClient, + status$: calculateStatus$(esNodesCompatibility$), }; } diff --git a/src/core/server/elasticsearch/index.ts b/src/core/server/elasticsearch/index.ts index cfd72a6fd5e47..2e45f710c4dcf 100644 --- a/src/core/server/elasticsearch/index.ts +++ b/src/core/server/elasticsearch/index.ts @@ -31,3 +31,4 @@ export { config, configSchema, ElasticsearchConfig } from './elasticsearch_confi export { ElasticsearchError, ElasticsearchErrorHelpers } from './errors'; export * from './api_types'; export * from './types'; +export { NodesVersionCompatibility } from './version_check/ensure_es_version'; diff --git a/src/core/server/elasticsearch/status.test.ts b/src/core/server/elasticsearch/status.test.ts new file mode 100644 index 0000000000000..dd5fb04bfd1c6 --- /dev/null +++ b/src/core/server/elasticsearch/status.test.ts @@ -0,0 +1,222 @@ +/* + * 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. + */ + +import { take } from 'rxjs/operators'; +import { Subject, of } from 'rxjs'; + +import { calculateStatus$ } from './status'; +import { ServiceStatusLevels, ServiceStatus } from '../status'; +import { ServiceStatusLevelSnapshotSerializer } from '../status/test_utils'; +import { NodesVersionCompatibility } from './version_check/ensure_es_version'; + +expect.addSnapshotSerializer(ServiceStatusLevelSnapshotSerializer); + +const nodeInfo = { + version: '1.1.1', + ip: '1.1.1.1', + http: { + publish_address: 'https://1.1.1.1:9200', + }, + name: 'node1', +}; + +describe('calculateStatus', () => { + it('starts in unavailable', async () => { + expect( + await calculateStatus$(new Subject()) + .pipe(take(1)) + .toPromise() + ).toEqual({ + level: ServiceStatusLevels.unavailable, + summary: 'Waiting for Elasticsearch', + meta: { + warningNodes: [], + incompatibleNodes: [], + }, + }); + }); + + it('changes to available when isCompatible and no warningNodes', async () => { + expect( + await calculateStatus$( + of({ isCompatible: true, kibanaVersion: '1.1.1', warningNodes: [], incompatibleNodes: [] }) + ) + .pipe(take(2)) + .toPromise() + ).toEqual({ + level: ServiceStatusLevels.available, + summary: 'Elasticsearch is available', + meta: { + warningNodes: [], + incompatibleNodes: [], + }, + }); + }); + + it('changes to degraded when isCompatible and warningNodes present', async () => { + expect( + await calculateStatus$( + of({ + isCompatible: true, + kibanaVersion: '1.1.1', + warningNodes: [nodeInfo], + incompatibleNodes: [], + // this isn't the real message, just used to test that the message + // is forwarded to the status + message: 'Some nodes are a different version', + }) + ) + .pipe(take(2)) + .toPromise() + ).toEqual({ + level: ServiceStatusLevels.degraded, + summary: 'Some nodes are a different version', + meta: { + incompatibleNodes: [], + warningNodes: [nodeInfo], + }, + }); + }); + + it('changes to critical when isCompatible is false', async () => { + expect( + await calculateStatus$( + of({ + isCompatible: false, + kibanaVersion: '2.1.1', + warningNodes: [nodeInfo], + incompatibleNodes: [nodeInfo], + // this isn't the real message, just used to test that the message + // is forwarded to the status + message: 'Incompatible with Elasticsearch', + }) + ) + .pipe(take(2)) + .toPromise() + ).toEqual({ + level: ServiceStatusLevels.critical, + summary: 'Incompatible with Elasticsearch', + meta: { + incompatibleNodes: [nodeInfo], + warningNodes: [nodeInfo], + }, + }); + }); + + it('emits status updates when node compatibility changes', () => { + const nodeCompat$ = new Subject(); + + const statusUpdates: ServiceStatus[] = []; + const subscription = calculateStatus$(nodeCompat$).subscribe(status => + statusUpdates.push(status) + ); + + nodeCompat$.next({ + isCompatible: false, + kibanaVersion: '2.1.1', + incompatibleNodes: [], + warningNodes: [], + message: 'Unable to retrieve version info', + }); + nodeCompat$.next({ + isCompatible: false, + kibanaVersion: '2.1.1', + incompatibleNodes: [nodeInfo], + warningNodes: [], + message: 'Incompatible with Elasticsearch', + }); + nodeCompat$.next({ + isCompatible: true, + kibanaVersion: '1.1.1', + warningNodes: [nodeInfo], + incompatibleNodes: [], + message: 'Some nodes are incompatible', + }); + nodeCompat$.next({ + isCompatible: true, + kibanaVersion: '1.1.1', + warningNodes: [], + incompatibleNodes: [], + }); + + subscription.unsubscribe(); + expect(statusUpdates).toMatchInlineSnapshot(` + Array [ + Object { + "level": unavailable, + "meta": Object { + "incompatibleNodes": Array [], + "warningNodes": Array [], + }, + "summary": "Waiting for Elasticsearch", + }, + Object { + "level": critical, + "meta": Object { + "incompatibleNodes": Array [], + "warningNodes": Array [], + }, + "summary": "Unable to retrieve version info", + }, + Object { + "level": critical, + "meta": Object { + "incompatibleNodes": Array [ + Object { + "http": Object { + "publish_address": "https://1.1.1.1:9200", + }, + "ip": "1.1.1.1", + "name": "node1", + "version": "1.1.1", + }, + ], + "warningNodes": Array [], + }, + "summary": "Incompatible with Elasticsearch", + }, + Object { + "level": degraded, + "meta": Object { + "incompatibleNodes": Array [], + "warningNodes": Array [ + Object { + "http": Object { + "publish_address": "https://1.1.1.1:9200", + }, + "ip": "1.1.1.1", + "name": "node1", + "version": "1.1.1", + }, + ], + }, + "summary": "Some nodes are incompatible", + }, + Object { + "level": available, + "meta": Object { + "incompatibleNodes": Array [], + "warningNodes": Array [], + }, + "summary": "Elasticsearch is available", + }, + ] + `); + }); +}); diff --git a/src/core/server/elasticsearch/status.ts b/src/core/server/elasticsearch/status.ts new file mode 100644 index 0000000000000..1eaa338af1239 --- /dev/null +++ b/src/core/server/elasticsearch/status.ts @@ -0,0 +1,78 @@ +/* + * 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. + */ + +import { Observable, merge, of } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { ServiceStatus, ServiceStatusLevels } from '../status'; +import { ElasticsearchStatusMeta } from './types'; +import { NodesVersionCompatibility } from './version_check/ensure_es_version'; + +export const calculateStatus$ = ( + esNodesCompatibility$: Observable +): Observable> => + merge( + of({ + level: ServiceStatusLevels.unavailable, + summary: `Waiting for Elasticsearch`, + meta: { + warningNodes: [], + incompatibleNodes: [], + }, + }), + esNodesCompatibility$.pipe( + map( + ({ + isCompatible, + message, + incompatibleNodes, + warningNodes, + }): ServiceStatus => { + if (!isCompatible) { + return { + level: ServiceStatusLevels.critical, + summary: + // Message should always be present, but this is a safe fallback + message ?? + `Some Elasticsearch nodes are not compatible with this version of Kibana`, + meta: { warningNodes, incompatibleNodes }, + }; + } else if (warningNodes.length > 0) { + return { + level: ServiceStatusLevels.degraded, + summary: + // Message should always be present, but this is a safe fallback + message ?? + `Some Elasticsearch nodes are running different versions than this version of Kibana`, + meta: { warningNodes, incompatibleNodes }, + }; + } + + return { + level: ServiceStatusLevels.available, + summary: `Elasticsearch is available`, + meta: { + warningNodes: [], + incompatibleNodes: [], + }, + }; + } + ) + ) + ); diff --git a/src/core/server/elasticsearch/types.ts b/src/core/server/elasticsearch/types.ts index ef8edecfd26ec..3d38935e9fbf0 100644 --- a/src/core/server/elasticsearch/types.ts +++ b/src/core/server/elasticsearch/types.ts @@ -22,6 +22,7 @@ import { ElasticsearchConfig } from './elasticsearch_config'; import { ElasticsearchClientConfig } from './elasticsearch_client_config'; import { IClusterClient, ICustomClusterClient } from './cluster_client'; import { NodesVersionCompatibility } from './version_check/ensure_es_version'; +import { ServiceStatus } from '../status'; /** * @public @@ -128,4 +129,11 @@ export interface InternalElasticsearchServiceSetup extends ElasticsearchServiceS readonly config$: Observable; }; esNodesCompatibility$: Observable; + status$: Observable>; +} + +/** @public */ +export interface ElasticsearchStatusMeta { + warningNodes: NodesVersionCompatibility['warningNodes']; + incompatibleNodes: NodesVersionCompatibility['incompatibleNodes']; } diff --git a/src/core/server/elasticsearch/version_check/ensure_es_version.ts b/src/core/server/elasticsearch/version_check/ensure_es_version.ts index 3e760ec0efabd..7bd6331978d1d 100644 --- a/src/core/server/elasticsearch/version_check/ensure_es_version.ts +++ b/src/core/server/elasticsearch/version_check/ensure_es_version.ts @@ -142,7 +142,7 @@ export const pollEsNodesVersion = ({ kibanaVersion, ignoreVersionMismatch, esVersionCheckInterval: healthCheckInterval, -}: PollEsNodesVersionOptions): Observable => { +}: PollEsNodesVersionOptions): Observable => { log.debug('Checking Elasticsearch version'); return timer(0, healthCheckInterval).pipe( exhaustMap(() => { diff --git a/src/core/server/index.ts b/src/core/server/index.ts index 56ce16a951aa2..a298f80f96d8f 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -60,6 +60,7 @@ import { import { CapabilitiesSetup, CapabilitiesStart } from './capabilities'; import { UuidServiceSetup } from './uuid'; import { MetricsServiceSetup } from './metrics'; +import { StatusServiceSetup } from './status'; export { bootstrap } from './bootstrap'; export { Capabilities, CapabilitiesProvider, CapabilitiesSwitcher } from './capabilities'; @@ -95,6 +96,8 @@ export { ElasticsearchErrorHelpers, ElasticsearchServiceSetup, ElasticsearchServiceStart, + ElasticsearchStatusMeta, + NodesVersionCompatibility, APICaller, FakeRequest, ScopeableRequest, @@ -226,6 +229,7 @@ export { SavedObjectsUpdateResponse, SavedObjectsServiceStart, SavedObjectsServiceSetup, + SavedObjectStatusMeta, SavedObjectsDeleteOptions, ISavedObjectsRepository, SavedObjectsRepository, @@ -294,6 +298,14 @@ export { LegacyInternals, } from './legacy'; +export { + CoreStatus, + ServiceStatus, + ServiceStatusLevel, + ServiceStatusLevels, + StatusServiceSetup, +} from './status'; + /** * Plugin specific context passed to a route handler. * @@ -348,14 +360,16 @@ export interface CoreSetup; } diff --git a/src/core/server/internal_types.ts b/src/core/server/internal_types.ts index 825deea99bc23..ede0d3dc9fcc7 100644 --- a/src/core/server/internal_types.ts +++ b/src/core/server/internal_types.ts @@ -31,6 +31,7 @@ import { import { InternalUiSettingsServiceSetup, InternalUiSettingsServiceStart } from './ui_settings'; import { UuidServiceSetup } from './uuid'; import { InternalMetricsServiceSetup } from './metrics'; +import { InternalStatusServiceSetup } from './status'; /** @internal */ export interface InternalCoreSetup { @@ -38,10 +39,11 @@ export interface InternalCoreSetup { context: ContextSetup; http: InternalHttpServiceSetup; elasticsearch: InternalElasticsearchServiceSetup; - uiSettings: InternalUiSettingsServiceSetup; + metrics: InternalMetricsServiceSetup; savedObjects: InternalSavedObjectsServiceSetup; + status: InternalStatusServiceSetup; + uiSettings: InternalUiSettingsServiceSetup; uuid: UuidServiceSetup; - metrics: InternalMetricsServiceSetup; } /** diff --git a/src/core/server/legacy/legacy_service.test.ts b/src/core/server/legacy/legacy_service.test.ts index c6860086e7784..0cf2ebe55ea10 100644 --- a/src/core/server/legacy/legacy_service.test.ts +++ b/src/core/server/legacy/legacy_service.test.ts @@ -48,6 +48,7 @@ import { findLegacyPluginSpecs } from './plugins'; import { LegacyVars, LegacyServiceSetupDeps, LegacyServiceStartDeps } from './types'; import { LegacyService } from './legacy_service'; import { coreMock } from '../mocks'; +import { statusServiceMock } from '../status/status_service.mock'; const MockKbnServer: jest.Mock = KbnServer as any; @@ -106,6 +107,7 @@ beforeEach(() => { rendering: renderingServiceMock, metrics: metricsServiceMock.createInternalSetupContract(), uuid: uuidSetup, + status: statusServiceMock.createInternalSetupContract(), }, plugins: { 'plugin-id': 'plugin-value' }, }; diff --git a/src/core/server/legacy/legacy_service.ts b/src/core/server/legacy/legacy_service.ts index bb5f6d5617aae..f77230301ce02 100644 --- a/src/core/server/legacy/legacy_service.ts +++ b/src/core/server/legacy/legacy_service.ts @@ -306,6 +306,9 @@ export class LegacyService implements CoreService { registerType: setupDeps.core.savedObjects.registerType, getImportExportObjectLimit: setupDeps.core.savedObjects.getImportExportObjectLimit, }, + status: { + core$: setupDeps.core.status.core$, + }, uiSettings: { register: setupDeps.core.uiSettings.register, }, diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts index 31bf17da041af..faf73044cac4d 100644 --- a/src/core/server/mocks.ts +++ b/src/core/server/mocks.ts @@ -33,6 +33,7 @@ import { InternalCoreSetup, InternalCoreStart } from './internal_types'; import { capabilitiesServiceMock } from './capabilities/capabilities_service.mock'; import { metricsServiceMock } from './metrics/metrics_service.mock'; import { uuidServiceMock } from './uuid/uuid_service.mock'; +import { statusServiceMock } from './status/status_service.mock'; export { httpServerMock } from './http/http_server.mocks'; export { sessionStorageMock } from './http/cookie_session_storage.mocks'; @@ -133,9 +134,10 @@ function createCoreSetupMock({ elasticsearch: elasticsearchServiceMock.createSetup(), http: httpMock, savedObjects: savedObjectsServiceMock.createInternalSetupContract(), + status: statusServiceMock.createSetupContract(), + metrics: metricsServiceMock.createSetupContract(), uiSettings: uiSettingsMock, uuid: uuidServiceMock.createSetupContract(), - metrics: metricsServiceMock.createSetupContract(), getStartServices: jest .fn, object, any]>, []>() .mockResolvedValue([createCoreStartMock(), pluginStartDeps, pluginStartContract]), @@ -161,10 +163,11 @@ function createInternalCoreSetupMock() { context: contextServiceMock.createSetupContract(), elasticsearch: elasticsearchServiceMock.createInternalSetup(), http: httpServiceMock.createSetupContract(), - uiSettings: uiSettingsServiceMock.createSetupContract(), + metrics: metricsServiceMock.createInternalSetupContract(), savedObjects: savedObjectsServiceMock.createInternalSetupContract(), + status: statusServiceMock.createInternalSetupContract(), uuid: uuidServiceMock.createSetupContract(), - metrics: metricsServiceMock.createInternalSetupContract(), + uiSettings: uiSettingsServiceMock.createSetupContract(), }; return setupDeps; } diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts index 32662f07a86f0..61d97aea97459 100644 --- a/src/core/server/plugins/plugin_context.ts +++ b/src/core/server/plugins/plugin_context.ts @@ -175,6 +175,9 @@ export function createPluginSetupContext( registerType: deps.savedObjects.registerType, getImportExportObjectLimit: deps.savedObjects.getImportExportObjectLimit, }, + status: { + core$: deps.status.core$, + }, uiSettings: { register: deps.uiSettings.register, }, diff --git a/src/core/server/saved_objects/index.ts b/src/core/server/saved_objects/index.ts index b50e47b9eab73..fe4795cad11a5 100644 --- a/src/core/server/saved_objects/index.ts +++ b/src/core/server/saved_objects/index.ts @@ -68,7 +68,11 @@ export { SavedObjectMigrationContext, } from './migrations'; -export { SavedObjectsType, SavedObjectsTypeManagementDefinition } from './types'; +export { + SavedObjectStatusMeta, + SavedObjectsType, + SavedObjectsTypeManagementDefinition, +} from './types'; export { savedObjectsConfig, savedObjectsMigrationConfig } from './saved_objects_config'; export { SavedObjectTypeRegistry, ISavedObjectTypeRegistry } from './saved_objects_type_registry'; diff --git a/src/core/server/saved_objects/migrations/core/index.ts b/src/core/server/saved_objects/migrations/core/index.ts index 4fbadf90f4b60..466d399f653cd 100644 --- a/src/core/server/saved_objects/migrations/core/index.ts +++ b/src/core/server/saved_objects/migrations/core/index.ts @@ -22,4 +22,4 @@ export { IndexMigrator } from './index_migrator'; export { buildActiveMappings } from './build_active_mappings'; export { CallCluster } from './call_cluster'; export { LogFn } from './migration_logger'; -export { MigrationResult } from './migration_coordinator'; +export { MigrationResult, MigrationStatus } from './migration_coordinator'; diff --git a/src/core/server/saved_objects/migrations/core/migration_coordinator.ts b/src/core/server/saved_objects/migrations/core/migration_coordinator.ts index ddd82edd93448..5ba2d0afc692e 100644 --- a/src/core/server/saved_objects/migrations/core/migration_coordinator.ts +++ b/src/core/server/saved_objects/migrations/core/migration_coordinator.ts @@ -39,6 +39,8 @@ import { SavedObjectsMigrationLogger } from './migration_logger'; const DEFAULT_POLL_INTERVAL = 15000; +export type MigrationStatus = 'waiting' | 'running' | 'completed'; + export type MigrationResult = | { status: 'skipped' } | { status: 'patched' } diff --git a/src/core/server/saved_objects/migrations/index.ts b/src/core/server/saved_objects/migrations/index.ts index dc966f0797822..8ddaed3707eb0 100644 --- a/src/core/server/saved_objects/migrations/index.ts +++ b/src/core/server/saved_objects/migrations/index.ts @@ -17,6 +17,7 @@ * under the License. */ +export { MigrationResult } from './core'; export { KibanaMigrator, IKibanaMigrator } from './kibana'; export { SavedObjectMigrationFn, diff --git a/src/core/server/saved_objects/migrations/kibana/index.ts b/src/core/server/saved_objects/migrations/kibana/index.ts index 25772c4c9b0b1..df4751521ac53 100644 --- a/src/core/server/saved_objects/migrations/kibana/index.ts +++ b/src/core/server/saved_objects/migrations/kibana/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { KibanaMigrator, IKibanaMigrator } from './kibana_migrator'; +export { KibanaMigrator, IKibanaMigrator, KibanaMigratorStatus } from './kibana_migrator'; diff --git a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.mock.ts b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.mock.ts index 2ee656721abd0..257b32c1e4c23 100644 --- a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.mock.ts +++ b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.mock.ts @@ -17,10 +17,11 @@ * under the License. */ -import { KibanaMigrator } from './kibana_migrator'; +import { KibanaMigrator, KibanaMigratorStatus } from './kibana_migrator'; import { buildActiveMappings } from '../core'; const { mergeTypes } = jest.requireActual('./kibana_migrator'); import { SavedObjectsType } from '../../types'; +import { BehaviorSubject } from 'rxjs'; const defaultSavedObjectTypes: SavedObjectsType[] = [ { @@ -47,6 +48,20 @@ const createMigrator = ( runMigrations: jest.fn(), getActiveMappings: jest.fn(), migrateDocument: jest.fn(), + getStatus$: jest.fn( + () => + new BehaviorSubject({ + status: 'completed', + result: [ + { + status: 'migrated', + destIndex: '.test-kibana_2', + sourceIndex: '.test-kibana_1', + elapsedMs: 10, + }, + ], + }) + ), }; mockMigrator.getActiveMappings.mockReturnValue(buildActiveMappings(mergeTypes(types))); diff --git a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.test.ts b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.test.ts index fd82bf282266e..336eeff99f47b 100644 --- a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.test.ts +++ b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.test.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import { take } from 'rxjs/operators'; import { KibanaMigratorOptions, KibanaMigrator } from './kibana_migrator'; import { loggingServiceMock } from '../../../logging/logging_service.mock'; @@ -79,6 +80,33 @@ describe('KibanaMigrator', () => { .filter(callClusterPath => callClusterPath === 'cat.templates'); expect(callClusterCommands.length).toBe(1); }); + + it('emits results on getMigratorResult$()', async () => { + const options = mockOptions(); + const clusterStub = jest.fn(() => ({ status: 404 })); + + options.callCluster = clusterStub; + const migrator = new KibanaMigrator(options); + const migratorStatus = migrator + .getStatus$() + .pipe(take(3)) + .toPromise(); + await migrator.runMigrations(); + const { status, result } = await migratorStatus; + expect(status).toEqual('completed'); + expect(result![0]).toMatchObject({ + destIndex: '.my-index_1', + elapsedMs: expect.any(Number), + sourceIndex: '.my-index', + status: 'migrated', + }); + expect(result![1]).toMatchObject({ + destIndex: 'other-index_1', + elapsedMs: expect.any(Number), + sourceIndex: 'other-index', + status: 'migrated', + }); + }); }); }); diff --git a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts index bc29061b380b8..dafd6c5341196 100644 --- a/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts +++ b/src/core/server/saved_objects/migrations/kibana/kibana_migrator.ts @@ -24,10 +24,17 @@ import { Logger } from 'src/core/server/logging'; import { KibanaConfigType } from 'src/core/server/kibana_config'; +import { BehaviorSubject } from 'rxjs'; import { IndexMapping, SavedObjectsTypeMappingDefinitions } from '../../mappings'; import { SavedObjectUnsanitizedDoc, SavedObjectsSerializer } from '../../serialization'; import { docValidator, PropertyValidators } from '../../validation'; -import { buildActiveMappings, CallCluster, IndexMigrator } from '../core'; +import { + buildActiveMappings, + CallCluster, + IndexMigrator, + MigrationResult, + MigrationStatus, +} from '../core'; import { DocumentMigrator, VersionedTransformer } from '../core/document_migrator'; import { createIndexMap } from '../core/build_index_map'; import { SavedObjectsMigrationConfigType } from '../../saved_objects_config'; @@ -46,6 +53,11 @@ export interface KibanaMigratorOptions { export type IKibanaMigrator = Pick; +export interface KibanaMigratorStatus { + status: MigrationStatus; + result?: MigrationResult[]; +} + /** * Manages the shape of mappings and documents in the Kibana index. */ @@ -58,7 +70,10 @@ export class KibanaMigrator { private readonly mappingProperties: SavedObjectsTypeMappingDefinitions; private readonly typeRegistry: ISavedObjectTypeRegistry; private readonly serializer: SavedObjectsSerializer; - private migrationResult?: Promise>; + private migrationResult?: Promise; + private readonly status$ = new BehaviorSubject({ + status: 'waiting', + }); /** * Creates an instance of KibanaMigrator. @@ -109,12 +124,20 @@ export class KibanaMigrator { Array<{ status: string }> > { if (this.migrationResult === undefined || rerun) { - this.migrationResult = this.runMigrationsInternal(); + this.status$.next({ status: 'running' }); + this.migrationResult = this.runMigrationsInternal().then(result => { + this.status$.next({ status: 'completed', result }); + return result; + }); } return this.migrationResult; } + public getStatus$() { + return this.status$.asObservable(); + } + private runMigrationsInternal() { const kibanaIndexName = this.kibanaConfig.index; const indexMap = createIndexMap({ diff --git a/src/core/server/saved_objects/saved_objects_service.mock.ts b/src/core/server/saved_objects/saved_objects_service.mock.ts index 9fe32b14e6450..7ba4613c857d7 100644 --- a/src/core/server/saved_objects/saved_objects_service.mock.ts +++ b/src/core/server/saved_objects/saved_objects_service.mock.ts @@ -17,6 +17,8 @@ * under the License. */ +import { BehaviorSubject } from 'rxjs'; + import { SavedObjectsService, InternalSavedObjectsServiceSetup, @@ -29,6 +31,7 @@ import { savedObjectsClientProviderMock } from './service/lib/scoped_client_prov import { savedObjectsRepositoryMock } from './service/lib/repository.mock'; import { savedObjectsClientMock } from './service/saved_objects_client.mock'; import { typeRegistryMock } from './saved_objects_type_registry.mock'; +import { ServiceStatusLevels } from '../status'; type SavedObjectsServiceContract = PublicMethodsOf; @@ -75,6 +78,10 @@ const createSetupContractMock = () => { const createInternalSetupContractMock = () => { const internalSetupContract: jest.Mocked = { ...createSetupContractMock(), + status$: new BehaviorSubject({ + level: ServiceStatusLevels.available, + summary: `SavedObjects is available`, + }), }; return internalSetupContract; }; diff --git a/src/core/server/saved_objects/saved_objects_service.ts b/src/core/server/saved_objects/saved_objects_service.ts index aa440c6454569..62027928c0bb5 100644 --- a/src/core/server/saved_objects/saved_objects_service.ts +++ b/src/core/server/saved_objects/saved_objects_service.ts @@ -17,8 +17,8 @@ * under the License. */ -import { Subject } from 'rxjs'; -import { first, filter, take } from 'rxjs/operators'; +import { Subject, Observable } from 'rxjs'; +import { first, filter, take, switchMap } from 'rxjs/operators'; import { CoreService } from '../../types'; import { SavedObjectsClient, @@ -38,7 +38,7 @@ import { SavedObjectConfig, } from './saved_objects_config'; import { KibanaRequest, InternalHttpServiceSetup } from '../http'; -import { SavedObjectsClientContract, SavedObjectsType } from './types'; +import { SavedObjectsClientContract, SavedObjectsType, SavedObjectStatusMeta } from './types'; import { ISavedObjectsRepository, SavedObjectsRepository } from './service/lib/repository'; import { SavedObjectsClientFactoryProvider, @@ -50,6 +50,8 @@ import { SavedObjectTypeRegistry, ISavedObjectTypeRegistry } from './saved_objec import { PropertyValidators } from './validation'; import { SavedObjectsSerializer } from './serialization'; import { registerRoutes } from './routes'; +import { ServiceStatus } from '../status'; +import { calculateStatus$ } from './status'; /** * Saved Objects is Kibana's data persistence mechanism allowing plugins to @@ -164,7 +166,9 @@ export interface SavedObjectsServiceSetup { /** * @internal */ -export type InternalSavedObjectsServiceSetup = SavedObjectsServiceSetup; +export interface InternalSavedObjectsServiceSetup extends SavedObjectsServiceSetup { + status$: Observable>; +} /** * Saved Objects is Kibana's data persisentence mechanism allowing plugins to @@ -321,6 +325,10 @@ export class SavedObjectsService }); return { + status$: calculateStatus$( + this.migrator$.pipe(switchMap(migrator => migrator.getStatus$())), + setupDeps.elasticsearch.status$ + ), setClientFactoryProvider: provider => { if (this.started) { throw new Error('cannot call `setClientFactoryProvider` after service startup.'); diff --git a/src/core/server/saved_objects/status.test.ts b/src/core/server/saved_objects/status.test.ts new file mode 100644 index 0000000000000..8efea1e2c00c6 --- /dev/null +++ b/src/core/server/saved_objects/status.test.ts @@ -0,0 +1,134 @@ +/* + * 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. + */ + +import { of, Observable } from 'rxjs'; +import { ServiceStatus, ServiceStatusLevels } from '../status'; +import { calculateStatus$ } from './status'; +import { take } from 'rxjs/operators'; + +describe('calculateStatus$', () => { + const expectUnavailableDueToEs = (status$: Observable) => + expect(status$.pipe(take(1)).toPromise()).resolves.toEqual({ + level: ServiceStatusLevels.unavailable, + summary: `SavedObjects service is not available without a healthy Elasticearch connection`, + }); + + const expectUnavailableDueToMigrations = (status$: Observable) => + expect(status$.pipe(take(1)).toPromise()).resolves.toEqual({ + level: ServiceStatusLevels.unavailable, + summary: `SavedObjects service is waiting to start migrations`, + }); + + describe('when elasticsearch is unavailable', () => { + const esStatus$ = of({ + level: ServiceStatusLevels.unavailable, + summary: 'xxx', + }); + + it('is unavailable before migrations have ran', async () => { + await expectUnavailableDueToEs(calculateStatus$(of(), esStatus$)); + }); + it('is unavailable after migrations have ran', async () => { + await expectUnavailableDueToEs( + calculateStatus$(of({ status: 'completed', result: [] }), esStatus$) + ); + }); + }); + + describe('when elasticsearch is critical', () => { + const esStatus$ = of({ + level: ServiceStatusLevels.critical, + summary: 'xxx', + }); + + it('is unavailable before migrations have ran', async () => { + await expectUnavailableDueToEs(calculateStatus$(of(), esStatus$)); + }); + it('is unavailable after migrations have ran', async () => { + await expectUnavailableDueToEs( + calculateStatus$( + of({ status: 'completed', result: [{ status: 'migrated' } as any] }), + esStatus$ + ) + ); + }); + }); + + describe('when elasticsearch is available', () => { + const esStatus$ = of({ + level: ServiceStatusLevels.available, + summary: 'Available', + }); + + it('is unavailable before migrations have ran', async () => { + await expectUnavailableDueToMigrations(calculateStatus$(of(), esStatus$)); + }); + it('is unavailable while migrations are running', async () => { + await expect( + calculateStatus$(of({ status: 'running' }), esStatus$) + .pipe(take(2)) + .toPromise() + ).resolves.toEqual({ + level: ServiceStatusLevels.unavailable, + summary: `SavedObjects service is running migrations`, + }); + }); + it('is available after migrations have ran', async () => { + await expect( + calculateStatus$( + of({ status: 'completed', result: [{ status: 'skipped' }, { status: 'patched' }] }), + esStatus$ + ) + .pipe(take(2)) + .toPromise() + ).resolves.toEqual({ + level: ServiceStatusLevels.available, + summary: `SavedObjects service has completed migrations and is available`, + meta: { + migratedIndices: { + migrated: 0, + patched: 1, + skipped: 1, + }, + }, + }); + }); + }); + + describe('when elasticsearch is degraded', () => { + const esStatus$ = of({ level: ServiceStatusLevels.degraded, summary: 'xxx' }); + + it('is unavailable before migrations have ran', async () => { + await expectUnavailableDueToMigrations(calculateStatus$(of(), esStatus$)); + }); + it('is degraded after migrations have ran', async () => { + await expect( + calculateStatus$( + of([{ status: 'skipped' }]), + esStatus$ + ) + .pipe(take(2)) + .toPromise() + ).resolves.toEqual({ + level: ServiceStatusLevels.degraded, + summary: 'SavedObjects service is degraded due to Elasticsearch: [xxx]', + }); + }); + }); +}); diff --git a/src/core/server/saved_objects/status.ts b/src/core/server/saved_objects/status.ts new file mode 100644 index 0000000000000..66a6e2baa17a7 --- /dev/null +++ b/src/core/server/saved_objects/status.ts @@ -0,0 +1,84 @@ +/* + * 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. + */ + +import { Observable, combineLatest } from 'rxjs'; +import { startWith, map } from 'rxjs/operators'; +import { ServiceStatus, ServiceStatusLevels } from '../status'; +import { SavedObjectStatusMeta } from './types'; +import { KibanaMigratorStatus } from './migrations/kibana'; + +export const calculateStatus$ = ( + rawMigratorStatus$: Observable, + elasticsearchStatus$: Observable +): Observable> => { + const migratorStatus$: Observable> = rawMigratorStatus$.pipe( + map(migrationStatus => { + if (migrationStatus.status === 'waiting') { + return { + level: ServiceStatusLevels.unavailable, + summary: `SavedObjects service is waiting to start migrations`, + }; + } else if (migrationStatus.status === 'running') { + return { + level: ServiceStatusLevels.unavailable, + summary: `SavedObjects service is running migrations`, + }; + } + + const statusCounts: SavedObjectStatusMeta['migratedIndices'] = { migrated: 0, skipped: 0 }; + if (migrationStatus.result) { + migrationStatus.result.forEach(({ status }) => { + statusCounts[status] = (statusCounts[status] ?? 0) + 1; + }); + } + + return { + level: ServiceStatusLevels.available, + summary: `SavedObjects service has completed migrations and is available`, + meta: { + migratedIndices: statusCounts, + }, + }; + }), + startWith({ + level: ServiceStatusLevels.unavailable, + summary: `SavedObjects service is waiting to start migrations`, + }) + ); + + return combineLatest([elasticsearchStatus$, migratorStatus$]).pipe( + map(([esStatus, migratorStatus]) => { + if (esStatus.level >= ServiceStatusLevels.unavailable) { + return { + level: ServiceStatusLevels.unavailable, + summary: `SavedObjects service is not available without a healthy Elasticearch connection`, + }; + } else if (migratorStatus.level === ServiceStatusLevels.unavailable) { + return migratorStatus; + } else if (esStatus.level === ServiceStatusLevels.degraded) { + return { + level: esStatus.level, + summary: `SavedObjects service is degraded due to Elasticsearch: [${esStatus.summary}]`, + }; + } else { + return migratorStatus; + } + }) + ); +}; diff --git a/src/core/server/saved_objects/types.ts b/src/core/server/saved_objects/types.ts index 962965a08f8b2..f14e9d9efb5e3 100644 --- a/src/core/server/saved_objects/types.ts +++ b/src/core/server/saved_objects/types.ts @@ -46,6 +46,19 @@ export { SavedObjectsMigrationVersion, } from '../../types'; +/** + * Meta information about the SavedObjectService's status. Available to plugins via {@link CoreSetup.status}. + * + * @public + */ +export interface SavedObjectStatusMeta { + migratedIndices: { + [status: string]: number; + skipped: number; + migrated: number; + }; +} + /** * * @public diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index e4e2b8d7adbb7..f3e3b7736d8d3 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -638,6 +638,8 @@ export interface CoreSetup ISavedObjectTypeRegistry; } +// @public +export interface SavedObjectStatusMeta { + // (undocumented) + migratedIndices: { + [status: string]: number; + skipped: number; + migrated: number; + }; +} + // @public (undocumented) export interface SavedObjectsType { convertToAliasScript?: string; @@ -2237,6 +2283,38 @@ export class ScopedClusterClient implements IScopedClusterClient { callAsInternalUser(endpoint: string, clientParams?: Record, options?: CallAPIOptions): Promise; } +// @public +export interface ServiceStatus | unknown = unknown> { + detail?: string; + documentationUrl?: string; + level: ServiceStatusLevel; + meta?: Meta; + summary: string; +} + +// @public +export type ServiceStatusLevel = typeof ServiceStatusLevels[keyof typeof ServiceStatusLevels]; + +// @public +export const ServiceStatusLevels: Readonly<{ + available: Readonly<{ + toString: () => "available"; + valueOf: () => 0; + }>; + degraded: Readonly<{ + toString: () => "degraded"; + valueOf: () => 1; + }>; + unavailable: Readonly<{ + toString: () => "unavailable"; + valueOf: () => 2; + }>; + critical: Readonly<{ + toString: () => "critical"; + valueOf: () => 3; + }>; +}>; + // @public export interface SessionCookieValidationResult { isValid: boolean; @@ -2274,6 +2352,11 @@ export type SharedGlobalConfig = RecursiveReadonly_2<{ // @public export type StartServicesAccessor = () => Promise<[CoreStart, TPluginsStart, TStart]>; +// @public +export interface StatusServiceSetup { + core$: Observable; +} + // @public export type StringValidation = StringValidationRegex | StringValidationRegexString; diff --git a/src/core/server/server.test.mocks.ts b/src/core/server/server.test.mocks.ts index 53d1b742a6494..5d535c9845724 100644 --- a/src/core/server/server.test.mocks.ts +++ b/src/core/server/server.test.mocks.ts @@ -85,3 +85,9 @@ export const mockMetricsService = metricsServiceMock.create(); jest.doMock('./metrics/metrics_service', () => ({ MetricsService: jest.fn(() => mockMetricsService), })); + +import { statusServiceMock } from './status/status_service.mock'; +export const mockStatusService = statusServiceMock.create(); +jest.doMock('./status/status_service', () => ({ + StatusService: jest.fn(() => mockStatusService), +})); diff --git a/src/core/server/server.test.ts b/src/core/server/server.test.ts index a4b5a9d81df20..24c41d511180a 100644 --- a/src/core/server/server.test.ts +++ b/src/core/server/server.test.ts @@ -29,6 +29,7 @@ import { mockUiSettingsService, mockRenderingService, mockMetricsService, + mockStatusService, } from './server.test.mocks'; import { BehaviorSubject } from 'rxjs'; @@ -63,6 +64,7 @@ test('sets up services on "setup"', async () => { expect(mockUiSettingsService.setup).not.toHaveBeenCalled(); expect(mockRenderingService.setup).not.toHaveBeenCalled(); expect(mockMetricsService.setup).not.toHaveBeenCalled(); + expect(mockStatusService.setup).not.toHaveBeenCalled(); await server.setup(); @@ -74,6 +76,7 @@ test('sets up services on "setup"', async () => { expect(mockUiSettingsService.setup).toHaveBeenCalledTimes(1); expect(mockRenderingService.setup).toHaveBeenCalledTimes(1); expect(mockMetricsService.setup).toHaveBeenCalledTimes(1); + expect(mockStatusService.setup).toHaveBeenCalledTimes(1); }); test('injects legacy dependency to context#setup()', async () => { @@ -141,6 +144,7 @@ test('stops services on "stop"', async () => { expect(mockSavedObjectsService.stop).not.toHaveBeenCalled(); expect(mockUiSettingsService.stop).not.toHaveBeenCalled(); expect(mockMetricsService.stop).not.toHaveBeenCalled(); + expect(mockStatusService.stop).not.toHaveBeenCalled(); await server.stop(); @@ -151,6 +155,7 @@ test('stops services on "stop"', async () => { expect(mockSavedObjectsService.stop).toHaveBeenCalledTimes(1); expect(mockUiSettingsService.stop).toHaveBeenCalledTimes(1); expect(mockMetricsService.stop).toHaveBeenCalledTimes(1); + expect(mockStatusService.stop).toHaveBeenCalledTimes(1); }); test(`doesn't setup core services if config validation fails`, async () => { @@ -167,6 +172,7 @@ test(`doesn't setup core services if config validation fails`, async () => { expect(mockUiSettingsService.setup).not.toHaveBeenCalled(); expect(mockRenderingService.setup).not.toHaveBeenCalled(); expect(mockMetricsService.setup).not.toHaveBeenCalled(); + expect(mockStatusService.setup).not.toHaveBeenCalled(); }); test(`doesn't setup core services if legacy config validation fails`, async () => { @@ -187,4 +193,5 @@ test(`doesn't setup core services if legacy config validation fails`, async () = expect(mockSavedObjectsService.stop).not.toHaveBeenCalled(); expect(mockUiSettingsService.setup).not.toHaveBeenCalled(); expect(mockMetricsService.setup).not.toHaveBeenCalled(); + expect(mockStatusService.setup).not.toHaveBeenCalled(); }); diff --git a/src/core/server/server.ts b/src/core/server/server.ts index 222be572b75e4..07ea431dd3a0d 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -36,6 +36,9 @@ import { UiSettingsService } from './ui_settings'; import { PluginsService, config as pluginsConfig } from './plugins'; import { SavedObjectsService } from '../server/saved_objects'; import { MetricsService, opsConfig } from './metrics'; +import { CapabilitiesService } from './capabilities'; +import { UuidService } from './uuid'; +import { StatusService } from './status/status_service'; import { config as cspConfig } from './csp'; import { config as elasticsearchConfig } from './elasticsearch'; @@ -50,8 +53,6 @@ import { mapToObject } from '../utils'; import { ContextService } from './context'; import { RequestHandlerContext } from '.'; import { InternalCoreSetup, InternalCoreStart } from './internal_types'; -import { CapabilitiesService } from './capabilities'; -import { UuidService } from './uuid'; const coreId = Symbol('core'); const rootConfigPath = ''; @@ -70,6 +71,7 @@ export class Server { private readonly uiSettings: UiSettingsService; private readonly uuid: UuidService; private readonly metrics: MetricsService; + private readonly status: StatusService; private readonly coreApp: CoreApp; private pluginsInitialized?: boolean; @@ -95,6 +97,7 @@ export class Server { this.capabilities = new CapabilitiesService(core); this.uuid = new UuidService(core); this.metrics = new MetricsService(core); + this.status = new StatusService(core); this.coreApp = new CoreApp(core); } @@ -145,15 +148,21 @@ export class Server { const metricsSetup = await this.metrics.setup({ http: httpSetup }); + const statusSetup = this.status.setup({ + elasticsearch: elasticsearchServiceSetup, + savedObjects: savedObjectsSetup, + }); + const coreSetup: InternalCoreSetup = { capabilities: capabilitiesSetup, context: contextServiceSetup, elasticsearch: elasticsearchServiceSetup, http: httpSetup, - uiSettings: uiSettingsSetup, + metrics: metricsSetup, savedObjects: savedObjectsSetup, + status: statusSetup, + uiSettings: uiSettingsSetup, uuid: uuidSetup, - metrics: metricsSetup, }; const pluginsSetup = await this.plugins.setup(coreSetup); @@ -220,6 +229,7 @@ export class Server { await this.uiSettings.stop(); await this.rendering.stop(); await this.metrics.stop(); + await this.status.stop(); } private registerCoreContext(coreSetup: InternalCoreSetup, rendering: RenderingServiceSetup) { diff --git a/src/core/server/status/get_summary_status.test.ts b/src/core/server/status/get_summary_status.test.ts new file mode 100644 index 0000000000000..7516e82ee784d --- /dev/null +++ b/src/core/server/status/get_summary_status.test.ts @@ -0,0 +1,180 @@ +/* + * 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. + */ + +import { ServiceStatus, ServiceStatusLevels } from './types'; +import { getSummaryStatus } from './get_summary_status'; + +describe('getSummaryStatus', () => { + const available: ServiceStatus = { level: ServiceStatusLevels.available, summary: 'Available' }; + const degraded: ServiceStatus = { + level: ServiceStatusLevels.degraded, + summary: 'This is degraded!', + }; + const unavailable: ServiceStatus = { + level: ServiceStatusLevels.unavailable, + summary: 'This is unavailable!', + }; + const critical: ServiceStatus = { + level: ServiceStatusLevels.critical, + summary: 'This is critical!', + }; + + it('returns available when all status are available', () => { + expect( + getSummaryStatus( + Object.entries({ + s1: available, + s2: available, + s3: available, + }) + ) + ).toMatchObject({ + level: ServiceStatusLevels.available, + }); + }); + + it('returns degraded when the worst status is degraded', () => { + expect( + getSummaryStatus( + Object.entries({ + s1: available, + s2: degraded, + s3: available, + }) + ) + ).toMatchObject({ + level: ServiceStatusLevels.degraded, + }); + }); + + it('returns unavailable when the worst status is unavailable', () => { + expect( + getSummaryStatus( + Object.entries({ + s1: available, + s2: degraded, + s3: unavailable, + }) + ) + ).toMatchObject({ + level: ServiceStatusLevels.unavailable, + }); + }); + + it('returns critical when the worst status is critical', () => { + expect( + getSummaryStatus( + Object.entries({ + s1: critical, + s2: degraded, + s3: unavailable, + }) + ) + ).toMatchObject({ + level: ServiceStatusLevels.critical, + }); + }); + + describe('summary', () => { + describe('when a single service is at highest level', () => { + it('returns all information about that single service', () => { + expect( + getSummaryStatus( + Object.entries({ + s1: degraded, + s2: { + level: ServiceStatusLevels.unavailable, + summary: 'Lorem ipsum', + detail: 'Vivamus pulvinar sem ac luctus ultrices.', + documentationUrl: 'http://helpmenow.com/problem1', + meta: { + custom: { data: 'here' }, + }, + }, + }) + ) + ).toEqual({ + level: ServiceStatusLevels.unavailable, + summary: '[s2]: Lorem ipsum', + detail: 'Vivamus pulvinar sem ac luctus ultrices.', + documentationUrl: 'http://helpmenow.com/problem1', + meta: { + custom: { data: 'here' }, + }, + }); + }); + }); + + describe('when multiple services is at highest level', () => { + it('returns aggregated information about the affected services', () => { + expect( + getSummaryStatus( + Object.entries({ + s1: degraded, + s2: { + level: ServiceStatusLevels.unavailable, + summary: 'Lorem ipsum', + detail: 'Vivamus pulvinar sem ac luctus ultrices.', + documentationUrl: 'http://helpmenow.com/problem1', + meta: { + custom: { data: 'here' }, + }, + }, + s3: { + level: ServiceStatusLevels.unavailable, + summary: 'Proin mattis', + detail: 'Nunc quis nulla at mi lobortis pretium.', + documentationUrl: 'http://helpmenow.com/problem2', + meta: { + other: { data: 'over there' }, + }, + }, + }) + ) + ).toEqual({ + level: ServiceStatusLevels.unavailable, + summary: '[2] services are unavailable', + detail: 'See the status page for more information', + meta: { + affectedServices: { + s2: { + level: ServiceStatusLevels.unavailable, + summary: 'Lorem ipsum', + detail: 'Vivamus pulvinar sem ac luctus ultrices.', + documentationUrl: 'http://helpmenow.com/problem1', + meta: { + custom: { data: 'here' }, + }, + }, + s3: { + level: ServiceStatusLevels.unavailable, + summary: 'Proin mattis', + detail: 'Nunc quis nulla at mi lobortis pretium.', + documentationUrl: 'http://helpmenow.com/problem2', + meta: { + other: { data: 'over there' }, + }, + }, + }, + }, + }); + }); + }); + }); +}); diff --git a/src/core/server/status/get_summary_status.ts b/src/core/server/status/get_summary_status.ts new file mode 100644 index 0000000000000..748a54f0bf8bb --- /dev/null +++ b/src/core/server/status/get_summary_status.ts @@ -0,0 +1,84 @@ +/* + * 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. + */ + +import { ServiceStatus, ServiceStatusLevels, ServiceStatusLevel } from './types'; + +/** + * Returns a single {@link ServiceStatus} that summarizes the most severe status level from a group of statuses. + * @param statuses + */ +export const getSummaryStatus = (statuses: Array<[string, ServiceStatus]>): ServiceStatus => { + const grouped = groupByLevel(statuses); + const highestSeverityLevel = getHighestSeverityLevel(grouped.keys()); + const highestSeverityGroup = grouped.get(highestSeverityLevel)!; + + if (highestSeverityLevel === ServiceStatusLevels.available) { + return { + level: ServiceStatusLevels.available, + summary: `All services are available`, + }; + } else if (highestSeverityGroup.size === 1) { + const [serviceName, status] = [...highestSeverityGroup.entries()][0]; + return { + ...status, + summary: `[${serviceName}]: ${status.summary!}`, + }; + } else { + return { + level: highestSeverityLevel, + summary: `[${highestSeverityGroup.size}] services are ${highestSeverityLevel.toString()}`, + // TODO: include URL to status page + detail: `See the status page for more information`, + meta: { + affectedServices: Object.fromEntries([...highestSeverityGroup]), + }, + }; + } +}; + +const groupByLevel = ( + statuses: Array<[string, ServiceStatus]> +): Map> => { + const byLevel = new Map>(); + + for (const [serviceName, status] of statuses) { + let levelMap = byLevel.get(status.level); + if (!levelMap) { + levelMap = new Map(); + byLevel.set(status.level, levelMap); + } + + levelMap.set(serviceName, status); + } + + return byLevel; +}; + +const getHighestSeverityLevel = (levels: Iterable): ServiceStatusLevel => { + const sorted = [...levels].sort((a, b) => { + if (a < b) { + return -1; + } else if (a > b) { + return 1; + } else { + return 0; + } + }); + return sorted[sorted.length - 1] ?? ServiceStatusLevels.available; +}; diff --git a/src/legacy/server/status/constants.js b/src/core/server/status/index.ts similarity index 90% rename from src/legacy/server/status/constants.js rename to src/core/server/status/index.ts index 3bb23749bae87..c39115d55a682 100644 --- a/src/legacy/server/status/constants.js +++ b/src/core/server/status/index.ts @@ -17,4 +17,5 @@ * under the License. */ -export const KIBANA_STATS_TYPE = 'oss_kibana_stats'; // kibana stats per 5s intervals +export { StatusService } from './status_service'; +export * from './types'; diff --git a/src/core/server/status/status_service.mock.ts b/src/core/server/status/status_service.mock.ts new file mode 100644 index 0000000000000..d550c2f06750b --- /dev/null +++ b/src/core/server/status/status_service.mock.ts @@ -0,0 +1,71 @@ +/* + * 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. + */ + +import { StatusService } from './status_service'; +import { + InternalStatusServiceSetup, + StatusServiceSetup, + ServiceStatusLevels, + ServiceStatus, + CoreStatus, +} from './types'; +import { BehaviorSubject } from 'rxjs'; + +const available: ServiceStatus = { + level: ServiceStatusLevels.available, + summary: 'Service is working', +}; +const availableCoreStatus: CoreStatus = { + elasticsearch: available, + savedObjects: available, +}; + +const createSetupContractMock = () => { + const setupContract: jest.Mocked = { + core$: new BehaviorSubject(availableCoreStatus), + }; + + return setupContract; +}; + +const createInternalSetupContractMock = () => { + const setupContract: jest.Mocked = { + core$: new BehaviorSubject(availableCoreStatus), + overall$: new BehaviorSubject(available), + }; + + return setupContract; +}; + +type StatusServiceContract = PublicMethodsOf; + +const createMock = () => { + const mocked: jest.Mocked = { + setup: jest.fn().mockReturnValue(createInternalSetupContractMock()), + start: jest.fn(), + stop: jest.fn(), + }; + return mocked; +}; + +export const statusServiceMock = { + create: createMock, + createSetupContract: createSetupContractMock, + createInternalSetupContract: createInternalSetupContractMock, +}; diff --git a/src/core/server/status/status_service.test.ts b/src/core/server/status/status_service.test.ts new file mode 100644 index 0000000000000..6d92a266369b9 --- /dev/null +++ b/src/core/server/status/status_service.test.ts @@ -0,0 +1,229 @@ +/* + * 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. + */ + +import { of, BehaviorSubject } from 'rxjs'; + +import { ServiceStatus, ServiceStatusLevels, CoreStatus } from './types'; +import { StatusService } from './status_service'; +import { first } from 'rxjs/operators'; +import { mockCoreContext } from '../core_context.mock'; +import { ServiceStatusLevelSnapshotSerializer } from './test_utils'; + +expect.addSnapshotSerializer(ServiceStatusLevelSnapshotSerializer); + +describe('StatusService', () => { + const available: ServiceStatus = { + level: ServiceStatusLevels.available, + summary: 'Available', + }; + const degraded: ServiceStatus = { + level: ServiceStatusLevels.degraded, + summary: 'This is degraded!', + }; + + describe('setup', () => { + describe('core$', () => { + it('rolls up core status observables into single observable', async () => { + const setup = new StatusService(mockCoreContext.create()).setup({ + elasticsearch: { + status$: of(available), + }, + savedObjects: { + status$: of(degraded), + }, + }); + expect(await setup.core$.pipe(first()).toPromise()).toEqual({ + elasticsearch: available, + savedObjects: degraded, + }); + }); + + it('replays last event', async () => { + const setup = new StatusService(mockCoreContext.create()).setup({ + elasticsearch: { + status$: of(available), + }, + savedObjects: { + status$: of(degraded), + }, + }); + const subResult1 = await setup.core$.pipe(first()).toPromise(); + const subResult2 = await setup.core$.pipe(first()).toPromise(); + const subResult3 = await setup.core$.pipe(first()).toPromise(); + expect(subResult1).toEqual({ + elasticsearch: available, + savedObjects: degraded, + }); + expect(subResult2).toEqual({ + elasticsearch: available, + savedObjects: degraded, + }); + expect(subResult3).toEqual({ + elasticsearch: available, + savedObjects: degraded, + }); + }); + + it('does not emit duplicate events', () => { + const elasticsearch$ = new BehaviorSubject(available); + const savedObjects$ = new BehaviorSubject(degraded); + const setup = new StatusService(mockCoreContext.create()).setup({ + elasticsearch: { + status$: elasticsearch$, + }, + savedObjects: { + status$: savedObjects$, + }, + }); + + const statusUpdates: CoreStatus[] = []; + const subscription = setup.core$.subscribe(status => statusUpdates.push(status)); + + elasticsearch$.next(available); + elasticsearch$.next(available); + elasticsearch$.next({ + level: ServiceStatusLevels.available, + summary: `Wow another summary`, + }); + savedObjects$.next(degraded); + savedObjects$.next(available); + savedObjects$.next(available); + subscription.unsubscribe(); + + expect(statusUpdates).toMatchInlineSnapshot(` + Array [ + Object { + "elasticsearch": Object { + "level": available, + "summary": "Available", + }, + "savedObjects": Object { + "level": degraded, + "summary": "This is degraded!", + }, + }, + Object { + "elasticsearch": Object { + "level": available, + "summary": "Wow another summary", + }, + "savedObjects": Object { + "level": degraded, + "summary": "This is degraded!", + }, + }, + Object { + "elasticsearch": Object { + "level": available, + "summary": "Wow another summary", + }, + "savedObjects": Object { + "level": available, + "summary": "Available", + }, + }, + ] + `); + }); + }); + + describe('overall$', () => { + it('exposes an overall summary', async () => { + const setup = new StatusService(mockCoreContext.create()).setup({ + elasticsearch: { + status$: of(degraded), + }, + savedObjects: { + status$: of(degraded), + }, + }); + expect(await setup.overall$.pipe(first()).toPromise()).toMatchObject({ + level: ServiceStatusLevels.degraded, + summary: '[2] services are degraded', + }); + }); + + it('replays last event', async () => { + const setup = new StatusService(mockCoreContext.create()).setup({ + elasticsearch: { + status$: of(degraded), + }, + savedObjects: { + status$: of(degraded), + }, + }); + const subResult1 = await setup.overall$.pipe(first()).toPromise(); + const subResult2 = await setup.overall$.pipe(first()).toPromise(); + const subResult3 = await setup.overall$.pipe(first()).toPromise(); + expect(subResult1).toMatchObject({ + level: ServiceStatusLevels.degraded, + summary: '[2] services are degraded', + }); + expect(subResult2).toMatchObject({ + level: ServiceStatusLevels.degraded, + summary: '[2] services are degraded', + }); + expect(subResult3).toMatchObject({ + level: ServiceStatusLevels.degraded, + summary: '[2] services are degraded', + }); + }); + + it('does not emit duplicate events', () => { + const elasticsearch$ = new BehaviorSubject(available); + const savedObjects$ = new BehaviorSubject(degraded); + const setup = new StatusService(mockCoreContext.create()).setup({ + elasticsearch: { + status$: elasticsearch$, + }, + savedObjects: { + status$: savedObjects$, + }, + }); + + const statusUpdates: ServiceStatus[] = []; + const subscription = setup.overall$.subscribe(status => statusUpdates.push(status)); + + elasticsearch$.next(available); + elasticsearch$.next(available); + elasticsearch$.next({ + level: ServiceStatusLevels.available, + summary: `Wow another summary`, + }); + savedObjects$.next(degraded); + savedObjects$.next(available); + savedObjects$.next(available); + subscription.unsubscribe(); + + expect(statusUpdates).toMatchInlineSnapshot(` + Array [ + Object { + "level": degraded, + "summary": "[savedObjects]: This is degraded!", + }, + Object { + "level": available, + "summary": "All services are available", + }, + ] + `); + }); + }); + }); +}); diff --git a/src/core/server/status/status_service.ts b/src/core/server/status/status_service.ts new file mode 100644 index 0000000000000..b6697d8221951 --- /dev/null +++ b/src/core/server/status/status_service.ts @@ -0,0 +1,78 @@ +/* + * 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. + */ + +/* eslint-disable max-classes-per-file */ + +import { Observable, combineLatest } from 'rxjs'; +import { map, distinctUntilChanged, shareReplay } from 'rxjs/operators'; +import { isDeepStrictEqual } from 'util'; + +import { CoreService } from '../../types'; +import { CoreContext } from '../core_context'; +import { Logger } from '../logging'; +import { InternalElasticsearchServiceSetup } from '../elasticsearch'; +import { InternalSavedObjectsServiceSetup } from '../saved_objects'; + +import { ServiceStatus, CoreStatus, InternalStatusServiceSetup } from './types'; +import { getSummaryStatus } from './get_summary_status'; + +interface SetupDeps { + elasticsearch: Pick; + savedObjects: Pick; +} + +export class StatusService implements CoreService { + private readonly logger: Logger; + + constructor(coreContext: CoreContext) { + this.logger = coreContext.logger.get('status'); + } + + public setup(core: SetupDeps) { + const core$ = this.setupCoreStatus(core); + const overall$: Observable = core$.pipe( + map(coreStatus => { + const summary = getSummaryStatus(Object.entries(coreStatus)); + this.logger.debug(`Recalculated overall status`, { status: summary }); + return summary; + }), + distinctUntilChanged(isDeepStrictEqual) + ); + + return { + core$, + overall$, + }; + } + + public start() {} + + public stop() {} + + private setupCoreStatus({ elasticsearch, savedObjects }: SetupDeps): Observable { + return combineLatest([elasticsearch.status$, savedObjects.status$]).pipe( + map(([elasticsearchStatus, savedObjectsStatus]) => ({ + elasticsearch: elasticsearchStatus, + savedObjects: savedObjectsStatus, + })), + distinctUntilChanged(isDeepStrictEqual), + shareReplay(1) + ); + } +} diff --git a/src/core/server/status/test_utils.ts b/src/core/server/status/test_utils.ts new file mode 100644 index 0000000000000..765fa8771f375 --- /dev/null +++ b/src/core/server/status/test_utils.ts @@ -0,0 +1,25 @@ +/* + * 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. + */ + +import { ServiceStatusLevels, ServiceStatusLevel } from './types'; + +export const ServiceStatusLevelSnapshotSerializer: jest.SnapshotSerializerPlugin = { + test: (val: any) => Object.values(ServiceStatusLevels).includes(val), + print: (val: ServiceStatusLevel) => val.toString(), +}; diff --git a/src/core/server/status/types.ts b/src/core/server/status/types.ts new file mode 100644 index 0000000000000..84a7356c66bbf --- /dev/null +++ b/src/core/server/status/types.ts @@ -0,0 +1,134 @@ +/* + * 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. + */ + +import { Observable } from 'rxjs'; +import { deepFreeze } from '../../utils'; + +/** + * The current status of a service at a point in time. + * + * @typeParam Meta - JSON-serializable object. Plugins should export this type to allow other plugins to read the `meta` + * field in a type-safe way. + * @public + */ +export interface ServiceStatus | unknown = unknown> { + /** + * The current availability level of the service. + */ + level: ServiceStatusLevel; + /** + * A high-level summary of the service status. + */ + summary: string; + /** + * A more detailed description of the service status. + */ + detail?: string; + /** + * A URL to open in a new tab about how to resolve or troubleshoot the problem. + */ + documentationUrl?: string; + /** + * Any JSON-serializable data to be included in the HTTP API response. Useful for providing more fine-grained, + * machine-readable information about the service status. May include status information for underlying features. + */ + meta?: Meta; +} + +/** + * The current "level" of availability of a service. + * + * @remarks + * The values implement `valueOf` to allow for easy comparisons between status levels with <, >, etc. Higher values + * represent higher severities. Note that the default `Array.prototype.sort` implementation does not correctly sort + * these values. + * + * A snapshot serializer is available in `src/core/server/test_utils` to ease testing of these values with Jest. + * + * @public + */ +export const ServiceStatusLevels = deepFreeze({ + /** + * Everything is working! + */ + available: { + toString: () => 'available', + valueOf: () => 0, + }, + /** + * Some features may not be working. + */ + degraded: { + toString: () => 'degraded', + valueOf: () => 1, + }, + /** + * The service is unavailable, but other functions that do not depend on this service should work. + */ + unavailable: { + toString: () => 'unavailable', + valueOf: () => 2, + }, + /** + * Block all user functions and display the status page, reserved for Core services only. + */ + critical: { + toString: () => 'critical', + valueOf: () => 3, + }, +}); + +/** + * A convenience type that represents the union of each value in {@link ServiceStatusLevels}. + * @public + */ +export type ServiceStatusLevel = typeof ServiceStatusLevels[keyof typeof ServiceStatusLevels]; + +/** + * Status of core services. + * + * @internalRemarks + * Only contains entries for backend services that could have a non-available `status`. + * For example, `context` cannot possibly be broken, so it is not included. + * + * @public + */ +export interface CoreStatus { + elasticsearch: ServiceStatus; + savedObjects: ServiceStatus; +} + +/** + * API for accessing status of Core and this plugin's dependencies as well as for customizing this plugin's status. + * @public + */ +export interface StatusServiceSetup { + /** + * Current status for all Core services. + */ + core$: Observable; +} + +/** @internal */ +export interface InternalStatusServiceSetup extends StatusServiceSetup { + /** + * Overall system status used for HTTP API + */ + overall$: Observable; +} diff --git a/src/core/server/test_utils.ts b/src/core/server/test_utils.ts index 470b1c2d135b7..f7e6fbcd0c131 100644 --- a/src/core/server/test_utils.ts +++ b/src/core/server/test_utils.ts @@ -18,3 +18,4 @@ */ export { createHttpServer } from './http/test_utils'; +export { ServiceStatusLevelSnapshotSerializer } from './status/test_utils'; diff --git a/src/dev/run_check_published_api_changes.ts b/src/dev/run_check_published_api_changes.ts index 8bb3fb20cea8b..ba3cd1280f34b 100644 --- a/src/dev/run_check_published_api_changes.ts +++ b/src/dev/run_check_published_api_changes.ts @@ -163,6 +163,7 @@ interface Options { accept: boolean; docs: boolean; help: boolean; + filter: string; } async function run( @@ -205,6 +206,7 @@ async function run( const extraFlags: string[] = []; const opts = (getopts(process.argv.slice(2), { boolean: ['accept', 'docs', 'help'], + string: ['filter'], default: { project: undefined, }, @@ -222,6 +224,8 @@ async function run( opts.help = true; } + const folders = ['core/public', 'core/server', 'plugins/data/server', 'plugins/data/public']; + if (opts.help) { process.stdout.write( dedent(chalk` @@ -240,9 +244,13 @@ async function run( {dim # Checks for and automatically accepts and updates documentation for any changes to the Kibana Core API} {dim $} node scripts/check_published_api_changes --accept + {dim # Only checks the core/public directory} + {dim $} node scripts/check_published_api_changes --filter=core/public + Options: --accept {dim Accepts all changes by updating the API Review files and documentation} --docs {dim Updates the Core API documentation} + --only {dim RegExp that folder names must match, folders: [${folders.join(', ')}]} --help {dim Show this message} `) ); @@ -258,9 +266,11 @@ async function run( return false; } - const folders = ['core/public', 'core/server', 'plugins/data/server', 'plugins/data/public']; - - const results = await Promise.all(folders.map(folder => run(folder, { log, opts }))); + const results = await Promise.all( + folders + .filter(folder => (opts.filter.length ? folder.match(opts.filter) : true)) + .map(folder => run(folder, { log, opts })) + ); if (results.find(r => r === false) !== undefined) { process.exitCode = 1; diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.tsx b/src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.tsx index 029e1f149d052..b7114f1029ef2 100644 --- a/src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.tsx +++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/controls_tab.tsx @@ -30,7 +30,7 @@ import { EuiSelect, } from '@elastic/eui'; -import { VisOptionsProps } from 'src/legacy/core_plugins/vis_default_editor/public'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { IIndexPattern } from 'src/plugins/data/public'; import { ControlEditor } from './control_editor'; import { diff --git a/src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.tsx b/src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.tsx index 95b14619c3416..cdff6cabad8ba 100644 --- a/src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.tsx +++ b/src/legacy/core_plugins/input_control_vis/public/components/editor/options_tab.tsx @@ -23,7 +23,7 @@ import { EuiForm, EuiFormRow, EuiSwitch } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiSwitchEvent } from '@elastic/eui'; -import { VisOptionsProps } from 'src/legacy/core_plugins/vis_default_editor/public'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; interface OptionsTabParams { updateFiltersOnChange: boolean; diff --git a/src/legacy/core_plugins/input_control_vis/public/input_control_vis_type.ts b/src/legacy/core_plugins/input_control_vis/public/input_control_vis_type.ts index 023e6ebb7125c..badea68eec19f 100644 --- a/src/legacy/core_plugins/input_control_vis/public/input_control_vis_type.ts +++ b/src/legacy/core_plugins/input_control_vis/public/input_control_vis_type.ts @@ -23,7 +23,7 @@ import { createInputControlVisController } from './vis_controller'; import { getControlsTab } from './components/editor/controls_tab'; import { OptionsTab } from './components/editor/options_tab'; import { InputControlVisDependencies } from './plugin'; -import { defaultFeedbackMessage } from '../../../../plugins/kibana_utils/common'; +import { defaultFeedbackMessage } from '../../../../plugins/kibana_utils/public'; export function createInputControlVisTypeDefinition(deps: InputControlVisDependencies) { const InputControlVisController = createInputControlVisController(deps); diff --git a/src/legacy/core_plugins/kibana/public/discover/build_services.ts b/src/legacy/core_plugins/kibana/public/discover/build_services.ts index 180ff13cdddc0..a3a99a0ded523 100644 --- a/src/legacy/core_plugins/kibana/public/discover/build_services.ts +++ b/src/legacy/core_plugins/kibana/public/discover/build_services.ts @@ -72,6 +72,7 @@ export async function buildServices( const services = { savedObjectsClient: core.savedObjects.client, indexPatterns: plugins.data.indexPatterns, + search: plugins.data.search, chrome: core.chrome, overlays: core.overlays, }; diff --git a/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts b/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts index 607d79b81618e..6ccbc13aeeb57 100644 --- a/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts +++ b/src/legacy/core_plugins/kibana/public/discover/get_inner_angular.ts @@ -201,7 +201,6 @@ function createDocTableModule() { .directive('docTable', createDocTableDirective) .directive('kbnTableHeader', createTableHeaderDirective) .directive('toolBarPagerText', createToolBarPagerTextDirective) - .directive('toolBarPagerText', createToolBarPagerTextDirective) .directive('kbnTableRow', createTableRowDirective) .directive('toolBarPagerButtons', createToolBarPagerButtonsDirective) .directive('kbnInfiniteScroll', createInfiniteScrollDirective) diff --git a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts index 55f369eaecd2c..98679a8f24d16 100644 --- a/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/discover/kibana_services.ts @@ -17,6 +17,8 @@ * under the License. */ import { DiscoverServices } from './build_services'; +import { createGetterSetter } from '../../../../../plugins/kibana_utils/public'; +import { search } from '../../../../../plugins/data/public'; let angularModule: any = null; let services: DiscoverServices | null = null; @@ -50,10 +52,6 @@ export const [getUrlTracker, setUrlTracker] = createGetterSetter<{ setTrackedUrl: (url: string) => void; }>('urlTracker'); -// EXPORT legacy static dependencies, should be migrated when available in a new version; -export { wrapInI18nContext } from 'ui/i18n'; -import { search } from '../../../../../plugins/data/public'; -import { createGetterSetter } from '../../../../../plugins/kibana_utils/common'; export const { getRequestInspectorStats, getResponseInspectorStats, tabifyAggResponse } = search; export { unhashUrl, diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/action_bar.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/action_bar.tsx index 57ad8e0b1040f..8fcfcba08955c 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/action_bar.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/action_bar.tsx @@ -18,7 +18,7 @@ */ import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; +import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; import { EuiButtonEmpty, EuiFieldNumber, @@ -88,77 +88,83 @@ export function ActionBar({ }; return ( -
- {isSuccessor && } - {isSuccessor && showWarning && } - {isSuccessor && showWarning && } - - - { - const value = newDocCount + defaultStepSize; - if (isValid(value)) { - setNewDocCount(value); - onChangeCount(value); - } - }} - flush="right" - > - - - - - - { - setNewDocCount(ev.target.valueAsNumber); - }} - onBlur={() => { - if (newDocCount !== docCount && isValid(newDocCount)) { - onChangeCount(newDocCount); + + + {isSuccessor && } + {isSuccessor && showWarning && ( + + )} + {isSuccessor && showWarning && } + + + { + const value = newDocCount + defaultStepSize; + if (isValid(value)) { + setNewDocCount(value); + onChangeCount(value); } }} - type="number" - value={newDocCount >= 0 ? newDocCount : ''} - /> - - - - - {isSuccessor ? ( - - ) : ( - + + + + + + { + setNewDocCount(ev.target.valueAsNumber); + }} + onBlur={() => { + if (newDocCount !== docCount && isValid(newDocCount)) { + onChangeCount(newDocCount); + } + }} + type="number" + value={newDocCount >= 0 ? newDocCount : ''} /> - )} - - - - {!isSuccessor && showWarning && } - {!isSuccessor && } - + + + + + {isSuccessor ? ( + + ) : ( + + )} + + + + {!isSuccessor && showWarning && ( + + )} + {!isSuccessor && } + + ); } diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/action_bar_directive.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/action_bar_directive.ts index 697b039adde81..b705b4e4faeb5 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/action_bar_directive.ts +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/context/components/action_bar/action_bar_directive.ts @@ -16,9 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -import { getAngularModule, wrapInI18nContext } from '../../../../../kibana_services'; +import { getAngularModule } from '../../../../../kibana_services'; import { ActionBar } from './action_bar'; getAngularModule().directive('contextActionBar', function(reactDirective: any) { - return reactDirective(wrapInI18nContext(ActionBar)); + return reactDirective(ActionBar); }); diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/index.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/index.js index 5482258e06564..5a999235bf9a5 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/index.js +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/index.js @@ -20,16 +20,12 @@ import { DiscoverNoResults } from './no_results'; import { DiscoverUninitialized } from './uninitialized'; import { DiscoverHistogram } from './histogram'; -import { getAngularModule, wrapInI18nContext } from '../../../kibana_services'; +import { getAngularModule } from '../../../kibana_services'; const app = getAngularModule(); -app.directive('discoverNoResults', reactDirective => - reactDirective(wrapInI18nContext(DiscoverNoResults)) -); +app.directive('discoverNoResults', reactDirective => reactDirective(DiscoverNoResults)); -app.directive('discoverUninitialized', reactDirective => - reactDirective(wrapInI18nContext(DiscoverUninitialized)) -); +app.directive('discoverUninitialized', reactDirective => reactDirective(DiscoverUninitialized)); app.directive('discoverHistogram', reactDirective => reactDirective(DiscoverHistogram)); diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/no_results.js b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/no_results.js index ba02068590c14..ad81d5252a25c 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/no_results.js +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/no_results.js @@ -18,7 +18,7 @@ */ import React, { Component, Fragment } from 'react'; -import { FormattedMessage } from '@kbn/i18n/react'; +import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; import PropTypes from 'prop-types'; import { @@ -247,29 +247,31 @@ export class DiscoverNoResults extends Component { } return ( - - + + + - - - - } - color="warning" - iconType="help" - data-test-subj="discoverNoResults" - /> + + + + } + color="warning" + iconType="help" + data-test-subj="discoverNoResults" + /> - {shardFailuresMessage} - {timeFieldMessage} - {luceneQueryMessage} - - - + {shardFailuresMessage} + {timeFieldMessage} + {luceneQueryMessage} + + + + ); } } diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/uninitialized.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/uninitialized.tsx index f40865800098e..b308607bbfbb0 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/uninitialized.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/uninitialized.tsx @@ -18,7 +18,7 @@ */ import React from 'react'; -import { FormattedMessage } from '@kbn/i18n/react'; +import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; import { EuiButton, EuiEmptyPrompt, EuiPage, EuiPageBody, EuiPageContent } from '@elastic/eui'; @@ -28,38 +28,40 @@ interface Props { export const DiscoverUninitialized = ({ onRefresh }: Props) => { return ( - - - - - - - } - body={ -

- -

- } - actions={ - - - - } - /> -
-
-
+ + + + + + + + } + body={ +

+ +

+ } + actions={ + + + + } + /> +
+
+
+
); }; diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.html b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.html index fb38f3e7d4c49..d068e824a3e0a 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.html +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/discover.html @@ -11,7 +11,7 @@

{{screenTitle}}

query="state.query" saved-query-id="state.savedQuery" screen-title="screenTitle" - show-date-picker="enableTimeRangeSelector" + show-date-picker="indexPattern.isTimeBased()" show-save-query="showSaveQuery" show-search-bar="true" use-default-behaviors="true" diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc.ts index 459dcfb30d17b..092e3c79b1007 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc.ts +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { getAngularModule, wrapInI18nContext, getServices } from '../../kibana_services'; +import { getAngularModule, getServices } from '../../kibana_services'; // @ts-ignore import { getRootBreadcrumbs } from '../helpers/breadcrumbs'; import html from './doc.html'; @@ -30,7 +30,7 @@ const { timefilter } = getServices(); const app = getAngularModule(); app.directive('discoverDoc', function(reactDirective: any) { return reactDirective( - wrapInI18nContext(Doc), + Doc, [ ['id', { watchDepth: 'value' }], ['index', { watchDepth: 'value' }], diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/index.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/index.ts index f21f3b17c6955..7e155a6b82ca0 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/index.ts +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/index.ts @@ -16,14 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -import { wrapInI18nContext } from '../../../../../kibana_services'; import { ToolBarPagerText } from './tool_bar_pager_text'; import { ToolBarPagerButtons } from './tool_bar_pager_buttons'; export function createToolBarPagerTextDirective(reactDirective: any) { - return reactDirective(wrapInI18nContext(ToolBarPagerText)); + return reactDirective(ToolBarPagerText); } export function createToolBarPagerButtonsDirective(reactDirective: any) { - return reactDirective(wrapInI18nContext(ToolBarPagerButtons)); + return reactDirective(ToolBarPagerButtons); } diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/tool_bar_pager_text.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/tool_bar_pager_text.tsx index 608dadd36b4b9..84338d817c86b 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/tool_bar_pager_text.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/pager/tool_bar_pager_text.tsx @@ -17,7 +17,7 @@ * under the License. */ import React from 'react'; -import { FormattedMessage } from '@kbn/i18n/react'; +import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; interface Props { startItem: number; @@ -27,12 +27,14 @@ interface Props { export function ToolBarPagerText({ startItem, endItem, totalItems }: Props) { return ( -
- -
+ +
+ +
+
); } diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header.ts index 84d865fd22a9a..5e7ad6a1d1ea0 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header.ts +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc_table/components/table_header.ts @@ -17,13 +17,13 @@ * under the License. */ import { TableHeader } from './table_header/table_header'; -import { wrapInI18nContext, getServices } from '../../../../kibana_services'; +import { getServices } from '../../../../kibana_services'; export function createTableHeaderDirective(reactDirective: any) { const { uiSettings: config } = getServices(); return reactDirective( - wrapInI18nContext(TableHeader), + TableHeader, [ ['columns', { watchDepth: 'collection' }], ['hideTimeColumn', { watchDepth: 'value' }], diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc/doc.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc/doc.tsx index 28a17dbdb67b7..f3ceaef57d700 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc/doc.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc/doc.tsx @@ -17,7 +17,7 @@ * under the License. */ import React from 'react'; -import { FormattedMessage } from '@kbn/i18n/react'; +import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; import { EuiCallOut, EuiLink, EuiLoadingSpinner, EuiPageContent } from '@elastic/eui'; import { IndexPatternsContract } from 'src/plugins/data/public'; import { ElasticRequestState, useEsDocSearch } from './use_es_doc_search'; @@ -65,83 +65,85 @@ export function Doc(props: DocProps) { const [reqState, hit, indexPattern] = useEsDocSearch(props); return ( - - {reqState === ElasticRequestState.NotFoundIndexPattern && ( - - } - /> - )} - {reqState === ElasticRequestState.NotFound && ( - - } - > - + + {reqState === ElasticRequestState.NotFoundIndexPattern && ( + + } /> - - )} - - {reqState === ElasticRequestState.Error && ( - + } + > - } - > - {' '} - + )} + + {reqState === ElasticRequestState.Error && ( + + } > - - - )} + id="kbn.doc.somethingWentWrongDescription" + defaultMessage="{indexName} is missing." + values={{ indexName: props.index }} + />{' '} + + + + + )} - {reqState === ElasticRequestState.Loading && ( - - {' '} - - - )} + {reqState === ElasticRequestState.Loading && ( + + {' '} + + + )} - {reqState === ElasticRequestState.Found && hit !== null && indexPattern && ( -
- -
- )} -
+ {reqState === ElasticRequestState.Found && hit !== null && indexPattern && ( +
+ +
+ )} + + ); } diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/fetch_error/fetch_error.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/fetch_error/fetch_error.tsx index 1aad7e953b8de..f8fc966dec351 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/fetch_error/fetch_error.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/fetch_error/fetch_error.tsx @@ -17,9 +17,9 @@ * under the License. */ import React, { Fragment } from 'react'; -import { FormattedMessage } from '@kbn/i18n/react'; +import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; import { EuiFlexGroup, EuiFlexItem, EuiCallOut, EuiCodeBlock, EuiSpacer } from '@elastic/eui'; -import { getAngularModule, wrapInI18nContext, getServices } from '../../../kibana_services'; +import { getAngularModule, getServices } from '../../../kibana_services'; interface Props { fetchError: { @@ -72,26 +72,28 @@ const DiscoverFetchError = ({ fetchError }: Props) => { } return ( - - + + + - - - - {body} + + + + {body} - {fetchError.error} - - - + {fetchError.error} + + + - - + + + ); }; export function createFetchErrorDirective(reactDirective: any) { - return reactDirective(wrapInI18nContext(DiscoverFetchError)); + return reactDirective(DiscoverFetchError); } getAngularModule().directive('discoverFetchError', createFetchErrorDirective); diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_sidebar.tsx b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_sidebar.tsx index 5984df9c76e61..0adda0e484843 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_sidebar.tsx +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_sidebar.tsx @@ -20,7 +20,7 @@ import React, { useCallback, useEffect, useState, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonIcon, EuiTitle } from '@elastic/eui'; import { sortBy } from 'lodash'; -import { FormattedMessage } from '@kbn/i18n/react'; +import { FormattedMessage, I18nProvider } from '@kbn/i18n/react'; import { DiscoverField } from './discover_field'; import { DiscoverIndexPattern } from './discover_index_pattern'; import { DiscoverFieldSearch } from './discover_field_search'; @@ -162,165 +162,175 @@ export function DiscoverSidebar({ } return ( -
- o.attributes.title)} - /> -
-
- - -
-
- {fields.length > 0 && ( - <> - -

- -

-
-
    - {selectedFields.map((field: IndexPatternField, idx: number) => { - return ( -
  • - -
  • - ); - })} -
-
- + +
+ o.attributes.title)} + /> +
+
+ + +
+
+ {fields.length > 0 && ( + <> +

-
- setShowFields(!showFields)} - aria-label={ - showFields - ? i18n.translate( - 'kbn.discover.fieldChooser.filter.indexAndFieldsSectionHideAriaLabel', - { - defaultMessage: 'Hide fields', - } - ) - : i18n.translate( - 'kbn.discover.fieldChooser.filter.indexAndFieldsSectionShowAriaLabel', - { - defaultMessage: 'Show fields', - } - ) - } - /> +
    + {selectedFields.map((field: IndexPatternField, idx: number) => { + return ( +
  • + +
  • + ); + })} +
+
+ +

+ +

+
+
+ setShowFields(!showFields)} + aria-label={ + showFields + ? i18n.translate( + 'kbn.discover.fieldChooser.filter.indexAndFieldsSectionHideAriaLabel', + { + defaultMessage: 'Hide fields', + } + ) + : i18n.translate( + 'kbn.discover.fieldChooser.filter.indexAndFieldsSectionShowAriaLabel', + { + defaultMessage: 'Show fields', + } + ) + } + /> +
+ + )} + {popularFields.length > 0 && ( +
+ + + +
    + {popularFields.map((field: IndexPatternField, idx: number) => { + return ( +
  • + +
  • + ); + })} +
- - )} - {popularFields.length > 0 && ( -
- - - -
    - {popularFields.map((field: IndexPatternField, idx: number) => { - return ( -
  • - -
  • - ); - })} -
-
- )} + )} -
    - {unpopularFields.map((field: IndexPatternField, idx: number) => { - return ( -
  • - -
  • - ); - })} -
-
-
+
    + {unpopularFields.map((field: IndexPatternField, idx: number) => { + return ( +
  • + +
  • + ); + })} +
+
+
+ ); } diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_sidebar_directive.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_sidebar_directive.ts index 9dcb459f83613..624ec0f757894 100644 --- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_sidebar_directive.ts +++ b/src/legacy/core_plugins/kibana/public/discover/np_ready/components/sidebar/discover_sidebar_directive.ts @@ -16,11 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -import { wrapInI18nContext } from '../../../kibana_services'; import { DiscoverSidebar } from './discover_sidebar'; export function createDiscoverSidebarDirective(reactDirective: any) { - return reactDirective(wrapInI18nContext(DiscoverSidebar), [ + return reactDirective(DiscoverSidebar, [ ['columns', { watchDepth: 'reference' }], ['fieldCounts', { watchDepth: 'reference' }], ['hits', { watchDepth: 'reference' }], diff --git a/src/legacy/core_plugins/management/public/index.ts b/src/legacy/core_plugins/kibana/public/index.ts similarity index 70% rename from src/legacy/core_plugins/management/public/index.ts rename to src/legacy/core_plugins/kibana/public/index.ts index bc3737524e125..a4fffc6eec26d 100644 --- a/src/legacy/core_plugins/management/public/index.ts +++ b/src/legacy/core_plugins/kibana/public/index.ts @@ -17,22 +17,7 @@ * under the License. */ -/** - * Static np-ready code, re-exported here so consumers can import from - * `src/legacy/core_plugins/management/public` - * - * @public - */ - export { - ManagementSetup, - ManagementStart, - plugin, - IndexPatternCreationConfig, - IndexPatternListConfig, -} from './np_ready'; - -export { - processImportResponse, ProcessedImportResponse, -} from '../../kibana/public/management/sections/objects/lib/process_import_response'; + processImportResponse, +} from './management/sections/objects/lib/process_import_response'; diff --git a/src/legacy/core_plugins/kibana/public/management/saved_object_registry.ts b/src/legacy/core_plugins/kibana/public/management/saved_object_registry.ts index 7261b2ba03372..705be68a141e7 100644 --- a/src/legacy/core_plugins/kibana/public/management/saved_object_registry.ts +++ b/src/legacy/core_plugins/kibana/public/management/saved_object_registry.ts @@ -56,6 +56,7 @@ export const savedObjectManagementRegistry: ISavedObjectsManagementRegistry = { const services = { savedObjectsClient: npStart.core.savedObjects.client, indexPatterns: npStart.plugins.data.indexPatterns, + search: npStart.plugins.data.search, chrome: npStart.core.chrome, overlays: npStart.core.overlays, }; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.test.tsx b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.test.tsx index 25bd36829b6d0..40471b95d774c 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.test.tsx +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.test.tsx @@ -21,7 +21,7 @@ import React from 'react'; import { StepIndexPattern } from '../step_index_pattern'; import { shallowWithI18nProvider } from 'test_utils/enzyme_helpers'; import { Header } from './components/header'; -import { IndexPatternCreationConfig } from '../../../../../../../../management/public'; +import { IndexPatternCreationConfig } from '../../../../../../../../../../plugins/index_pattern_management/public'; import { coreMock } from '../../../../../../../../../../core/public/mocks'; import { dataPluginMock } from '../../../../../../../../../../plugins/data/public/mocks'; import { SavedObjectsFindResponsePublic } from '../../../../../../../../../../core/public'; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx index bbb6bf26e5b31..648bf7f8f9738 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_index_pattern/step_index_pattern.tsx @@ -39,7 +39,7 @@ import { LoadingIndices } from './components/loading_indices'; import { StatusMessage } from './components/status_message'; import { IndicesList } from './components/indices_list'; import { Header } from './components/header'; -import { IndexPatternCreationConfig } from '../../../../../../../../management/public'; +import { IndexPatternCreationConfig } from '../../../../../../../../../../plugins/index_pattern_management/public'; import { MatchedIndex } from '../../types'; interface StepIndexPatternProps { diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/step_time_field.test.tsx b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/step_time_field.test.tsx index e0c43105cb320..b23b1e3ad9051 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/step_time_field.test.tsx +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/step_time_field.test.tsx @@ -19,7 +19,7 @@ import React from 'react'; import { shallowWithI18nProvider } from 'test_utils/enzyme_helpers'; -import { IndexPatternCreationConfig } from '../../../../../../../../management/public'; +import { IndexPatternCreationConfig } from '../../../../../../../../../../plugins/index_pattern_management/public'; import { IFieldType } from '../../../../../../../../../../plugins/data/public'; import { StepTimeField } from '../step_time_field'; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/step_time_field.tsx b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/step_time_field.tsx index 80582cc1fbd92..a58bf10c9dab8 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/step_time_field.tsx +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/components/step_time_field/step_time_field.tsx @@ -34,7 +34,7 @@ import { Header } from './components/header'; import { TimeField } from './components/time_field'; import { AdvancedOptions } from './components/advanced_options'; import { ActionButtons } from './components/action_buttons'; -import { IndexPatternCreationConfig } from '../../../../../../../../management/public'; +import { IndexPatternCreationConfig } from '../../../../../../../../../../plugins/index_pattern_management/public'; import { DataPublicPluginStart } from '../../../../../../../../../../plugins/data/public'; interface StepTimeFieldProps { diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js index 50c5a58d35db3..47cb773258cb4 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/index.js @@ -20,7 +20,6 @@ import uiRoutes from 'ui/routes'; import angularTemplate from './angular_template.html'; import { npStart } from 'ui/new_platform'; -import { setup as managementSetup } from '../../../../../../management/public/legacy'; import { getCreateBreadcrumbs } from '../breadcrumbs'; import { renderCreateIndexPatternWizard, destroyCreateIndexPatternWizard } from './render'; @@ -33,7 +32,7 @@ uiRoutes.when('/management/kibana/index_pattern', { const kbnUrl = $injector.get('kbnUrl'); $scope.$$postDigest(() => { const $routeParams = $injector.get('$routeParams'); - const indexPatternCreationType = managementSetup.indexPattern.creation.getType( + const indexPatternCreationType = npStart.plugins.indexPatternManagement.creation.getType( $routeParams.type ); const services = { diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.test.ts b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.test.ts index 5a8460fcb51ba..40583af7177fe 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.test.ts +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.test.ts @@ -18,7 +18,7 @@ */ import { getIndices } from './get_indices'; -import { IndexPatternCreationConfig } from './../../../../../../../management/public'; +import { IndexPatternCreationConfig } from '../../../../../../../../../plugins/index_pattern_management/public'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { LegacyApiCaller } from '../../../../../../../../../plugins/data/public/search'; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.ts b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.ts index 3848c425e2d49..3b1b7a3b52a5b 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.ts +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/lib/get_indices.ts @@ -18,7 +18,7 @@ */ import { get, sortBy } from 'lodash'; -import { IndexPatternCreationConfig } from '../../../../../../../management/public'; +import { IndexPatternCreationConfig } from '../../../../../../../../../plugins/index_pattern_management/public'; import { DataPublicPluginStart } from '../../../../../../../../../plugins/data/public'; import { MatchedIndex } from '../types'; diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/edit_index_pattern.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/edit_index_pattern.js index 6d302ac5a74f3..594430ca01f4c 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/edit_index_pattern.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/edit_index_pattern/edit_index_pattern.js @@ -29,7 +29,6 @@ import { uiModules } from 'ui/modules'; import template from './edit_index_pattern.html'; import { fieldWildcardMatcher } from '../../../../../../../../plugins/kibana_utils/public'; import { subscribeWithScope } from '../../../../../../../../plugins/kibana_legacy/public'; -import { setup as managementSetup } from '../../../../../../management/public/legacy'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; import { SourceFiltersTable } from './source_filters_table'; @@ -239,14 +238,12 @@ uiModules $scope.editSectionsProvider = Private(IndicesEditSectionsProvider); $scope.kbnUrl = Private(KbnUrlProvider); $scope.indexPattern = $route.current.locals.indexPattern; - $scope.indexPatternListProvider = managementSetup.indexPattern.list; - $scope.indexPattern.tags = managementSetup.indexPattern.list.getIndexPatternTags( + $scope.indexPatternListProvider = npStart.plugins.indexPatternManagement.list; + $scope.indexPattern.tags = npStart.plugins.indexPatternManagement.list.getIndexPatternTags( $scope.indexPattern, $scope.indexPattern.id === config.get('defaultIndex') ); - $scope.getFieldInfo = managementSetup.indexPattern.list.getFieldInfo.bind( - managementSetup.indexPattern.list - ); + $scope.getFieldInfo = npStart.plugins.indexPatternManagement.list.getFieldInfo; docTitle.change($scope.indexPattern.title); const otherPatterns = _.filter($route.current.locals.indexPatterns, pattern => { @@ -257,7 +254,7 @@ uiModules $scope.editSections = $scope.editSectionsProvider( $scope.indexPattern, $scope.fieldFilter, - managementSetup.indexPattern.list + npStart.plugins.indexPatternManagement.list ); $scope.refreshFilters(); $scope.fields = $scope.indexPattern.getNonScriptedFields(); @@ -363,7 +360,7 @@ uiModules $scope.editSections = $scope.editSectionsProvider( $scope.indexPattern, $scope.fieldFilter, - managementSetup.indexPattern.list + npStart.plugins.indexPatternManagement.list ); if ($scope.fieldFilter === undefined) { diff --git a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js index 310797a7f3a0c..a8376c0e84bf9 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/index_patterns/index.js @@ -18,7 +18,6 @@ */ import { management } from 'ui/management'; -import { setup as managementSetup } from '../../../../../management/public/legacy'; import './create_index_pattern_wizard'; import './edit_index_pattern'; import uiRoutes from 'ui/routes'; @@ -111,7 +110,7 @@ uiModules transclude: true, template: indexTemplate, link: async function($scope) { - const indexPatternCreationOptions = await managementSetup.indexPattern.creation.getIndexPatternCreationOptions( + const indexPatternCreationOptions = await npStart.plugins.indexPatternManagement.creation.getIndexPatternCreationOptions( url => { $scope.$evalAsync(() => kbnUrl.change(url)); } @@ -124,7 +123,7 @@ uiModules const id = pattern.id; const title = pattern.get('title'); const isDefault = $scope.defaultIndex === id; - const tags = managementSetup.indexPattern.list.getIndexPatternTags( + const tags = npStart.plugins.indexPatternManagement.list.getIndexPatternTags( pattern, isDefault ); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/__jest__/objects_table.test.js b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/__jest__/objects_table.test.js index a5e34f8955fe3..7b9c17640a0f3 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/__jest__/objects_table.test.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/__jest__/objects_table.test.js @@ -19,7 +19,7 @@ import React from 'react'; import { shallowWithI18nProvider } from 'test_utils/enzyme_helpers'; -import { mockManagementPlugin } from '../../../../../../../../management/public/np_ready/mocks'; +import { mockManagementPlugin } from '../../../../../../../../../../plugins/index_pattern_management/public/mocks'; import { Query } from '@elastic/eui'; import { ObjectsTable, POSSIBLE_TYPES } from '../objects_table'; @@ -30,7 +30,7 @@ import { extractExportDetails } from '../../../lib/extract_export_details'; jest.mock('ui/kfetch', () => ({ kfetch: jest.fn() })); -jest.mock('../../../../../../../../management/public/legacy', () => ({ +jest.mock('../../../../../../../../../../plugins/index_pattern_management/public', () => ({ setup: mockManagementPlugin.createSetupContract(), start: mockManagementPlugin.createStartContract(), })); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/__jest__/flyout.test.js b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/__jest__/flyout.test.js index 97c0d5b89d657..0d16e0ae35dd6 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/__jest__/flyout.test.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/__jest__/flyout.test.js @@ -19,7 +19,7 @@ import React from 'react'; import { shallowWithI18nProvider } from 'test_utils/enzyme_helpers'; -import { mockManagementPlugin } from '../../../../../../../../../../management/public/np_ready/mocks'; +import { mockManagementPlugin } from '../../../../../../../../../../../../plugins/index_pattern_management/public/mocks'; import { Flyout } from '../flyout'; jest.mock('ui/kfetch', () => ({ kfetch: jest.fn() })); @@ -48,7 +48,7 @@ jest.mock('../../../../../lib/resolve_saved_objects', () => ({ saveObjects: jest.fn(), })); -jest.mock('../../../../../../../../../../management/public/legacy', () => ({ +jest.mock('../../../../../../../../../../../../plugins/index_pattern_management/public', () => ({ setup: mockManagementPlugin.createSetupContract(), start: mockManagementPlugin.createStartContract(), })); @@ -519,7 +519,8 @@ describe('Flyout', () => { expect(resolveIndexPatternConflicts).toHaveBeenCalledWith( component.instance().resolutions, mockConflictedIndexPatterns, - true + true, + defaultProps.indexPatterns ); expect(saveObjects).toHaveBeenCalledWith( mockConflictedSavedObjectsLinkedToSavedSearches, diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/flyout.js b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/flyout.js index 105c279218375..da2221bb54203 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/flyout.js +++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/components/objects_table/components/flyout/flyout.js @@ -358,7 +358,8 @@ export class Flyout extends Component { importCount += await resolveIndexPatternConflicts( resolutions, conflictedIndexPatterns, - isOverwriteAllChecked + isOverwriteAllChecked, + this.props.indexPatterns ); } this.setState({ diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_saved_objects.test.ts b/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_saved_objects.test.ts index 8243aa69ac082..dc6d2643145ff 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_saved_objects.test.ts +++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_saved_objects.test.ts @@ -84,7 +84,7 @@ describe('resolveSavedObjects', () => { }, } as unknown) as IndexPatternsContract; - const services = [ + const services = ([ { type: 'search', get: async () => { @@ -124,7 +124,7 @@ describe('resolveSavedObjects', () => { }; }, }, - ] as SavedObjectLoader[]; + ] as unknown) as SavedObjectLoader[]; const overwriteAll = false; @@ -176,7 +176,7 @@ describe('resolveSavedObjects', () => { }, } as unknown) as IndexPatternsContract; - const services = [ + const services = ([ { type: 'search', get: async () => { @@ -217,7 +217,7 @@ describe('resolveSavedObjects', () => { }; }, }, - ] as SavedObjectLoader[]; + ] as unknown) as SavedObjectLoader[]; const overwriteAll = false; @@ -237,33 +237,38 @@ describe('resolveSavedObjects', () => { describe('resolveIndexPatternConflicts', () => { it('should resave resolutions', async () => { - const hydrateIndexPattern = jest.fn(); const save = jest.fn(); - const conflictedIndexPatterns = [ + const conflictedIndexPatterns = ([ { obj: { - searchSource: { - getOwnField: (field: string) => { - return field === 'index' ? '1' : undefined; + save, + }, + doc: { + _source: { + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ + index: '1', + }), }, }, - hydrateIndexPattern, - save, }, }, { obj: { - searchSource: { - getOwnField: (field: string) => { - return field === 'index' ? '3' : undefined; + save, + }, + doc: { + _source: { + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ + index: '3', + }), }, }, - hydrateIndexPattern, - save, }, }, - ]; + ] as unknown) as Array<{ obj: SavedObject; doc: any }>; const resolutions = [ { @@ -282,43 +287,49 @@ describe('resolveSavedObjects', () => { const overwriteAll = false; - await resolveIndexPatternConflicts(resolutions, conflictedIndexPatterns, overwriteAll); - expect(hydrateIndexPattern.mock.calls.length).toBe(2); + await resolveIndexPatternConflicts(resolutions, conflictedIndexPatterns, overwriteAll, ({ + get: (id: string) => Promise.resolve({ id }), + } as unknown) as IndexPatternsContract); + expect(conflictedIndexPatterns[0].obj.searchSource!.getField('index')!.id).toEqual('2'); + expect(conflictedIndexPatterns[1].obj.searchSource!.getField('index')!.id).toEqual('4'); expect(save.mock.calls.length).toBe(2); expect(save).toHaveBeenCalledWith({ confirmOverwrite: !overwriteAll }); - expect(hydrateIndexPattern).toHaveBeenCalledWith('2'); - expect(hydrateIndexPattern).toHaveBeenCalledWith('4'); }); it('should resolve filter index conflicts', async () => { - const hydrateIndexPattern = jest.fn(); const save = jest.fn(); - const conflictedIndexPatterns = [ + const conflictedIndexPatterns = ([ { obj: { - searchSource: { - getOwnField: (field: string) => { - return field === 'index' ? '1' : [{ meta: { index: 'filterIndex' } }]; + save, + }, + doc: { + _source: { + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ + index: '1', + filter: [{ meta: { index: 'filterIndex' } }], + }), }, - setField: jest.fn(), }, - hydrateIndexPattern, - save, }, }, { obj: { - searchSource: { - getOwnField: (field: string) => { - return field === 'index' ? '3' : undefined; + save, + }, + doc: { + _source: { + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ + index: '3', + }), }, }, - hydrateIndexPattern, - save, }, }, - ]; + ] as unknown) as Array<{ obj: SavedObject; doc: any }>; const resolutions = [ { @@ -337,9 +348,11 @@ describe('resolveSavedObjects', () => { const overwriteAll = false; - await resolveIndexPatternConflicts(resolutions, conflictedIndexPatterns, overwriteAll); + await resolveIndexPatternConflicts(resolutions, conflictedIndexPatterns, overwriteAll, ({ + get: (id: string) => Promise.resolve({ id }), + } as unknown) as IndexPatternsContract); - expect(conflictedIndexPatterns[0].obj.searchSource.setField).toHaveBeenCalledWith('filter', [ + expect(conflictedIndexPatterns[0].obj.searchSource!.getField('filter')).toEqual([ { meta: { index: 'newFilterIndex' } }, ]); expect(save.mock.calls.length).toBe(2); diff --git a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_saved_objects.ts b/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_saved_objects.ts index 902de654f5f85..d9473367f7502 100644 --- a/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_saved_objects.ts +++ b/src/legacy/core_plugins/kibana/public/management/sections/objects/lib/resolve_saved_objects.ts @@ -18,12 +18,17 @@ */ import { i18n } from '@kbn/i18n'; -import { OverlayStart } from 'src/core/public'; +import { cloneDeep } from 'lodash'; +import { OverlayStart, SavedObjectReference } from 'src/core/public'; import { SavedObject, SavedObjectLoader, } from '../../../../../../../../plugins/saved_objects/public'; -import { IndexPatternsContract, IIndexPattern } from '../../../../../../../../plugins/data/public'; +import { + IndexPatternsContract, + IIndexPattern, + createSearchSource, +} from '../../../../../../../../plugins/data/public'; type SavedObjectsRawDoc = Record; @@ -126,7 +131,7 @@ async function importIndexPattern( async function importDocument(obj: SavedObject, doc: SavedObjectsRawDoc, overwriteAll: boolean) { await obj.applyESResp({ references: doc._references || [], - ...doc, + ...cloneDeep(doc), }); return await obj.save({ confirmOverwrite: !overwriteAll }); } @@ -160,41 +165,57 @@ async function awaitEachItemInParallel(list: T[], op: (item: T) => R) { export async function resolveIndexPatternConflicts( resolutions: Array<{ oldId: string; newId: string }>, conflictedIndexPatterns: any[], - overwriteAll: boolean + overwriteAll: boolean, + indexPatterns: IndexPatternsContract ) { let importCount = 0; - await awaitEachItemInParallel(conflictedIndexPatterns, async ({ obj }) => { - // Resolve search index reference: - let oldIndexId = obj.searchSource.getOwnField('index'); - // Depending on the object, this can either be the raw id or the actual index pattern object - if (typeof oldIndexId !== 'string') { - oldIndexId = oldIndexId.id; - } - let resolution = resolutions.find(({ oldId }) => oldId === oldIndexId); - if (resolution) { - const newIndexId = resolution.newId; - await obj.hydrateIndexPattern(newIndexId); + await awaitEachItemInParallel(conflictedIndexPatterns, async ({ obj, doc }) => { + const serializedSearchSource = JSON.parse( + doc._source.kibanaSavedObjectMeta?.searchSourceJSON || '{}' + ); + const oldIndexId = serializedSearchSource.index; + let allResolved = true; + const inlineResolution = resolutions.find(({ oldId }) => oldId === oldIndexId); + if (inlineResolution) { + serializedSearchSource.index = inlineResolution.newId; + } else { + allResolved = false; } // Resolve filter index reference: - const filter = (obj.searchSource.getOwnField('filter') || []).map((f: any) => { + const filter = (serializedSearchSource.filter || []).map((f: any) => { if (!(f.meta && f.meta.index)) { return f; } - resolution = resolutions.find(({ oldId }) => oldId === f.meta.index); + const resolution = resolutions.find(({ oldId }) => oldId === f.meta.index); return resolution ? { ...f, ...{ meta: { ...f.meta, index: resolution.newId } } } : f; }); if (filter.length > 0) { - obj.searchSource.setField('filter', filter); + serializedSearchSource.filter = filter; } - if (!resolution) { + const replacedReferences = (doc._references || []).map((reference: SavedObjectReference) => { + const resolution = resolutions.find(({ oldId }) => oldId === reference.id); + if (resolution) { + return { ...reference, id: resolution.newId }; + } else { + allResolved = false; + } + + return reference; + }); + + if (!allResolved) { // The user decided to skip this conflict so do nothing return; } + obj.searchSource = await createSearchSource(indexPatterns)( + JSON.stringify(serializedSearchSource), + replacedReferences + ); if (await saveObject(obj, overwriteAll)) { importCount++; } diff --git a/src/legacy/core_plugins/kibana/public/visualize/_index.scss b/src/legacy/core_plugins/kibana/public/visualize/_index.scss index 0632831578bd0..079d82936bb57 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/_index.scss +++ b/src/legacy/core_plugins/kibana/public/visualize/_index.scss @@ -1,2 +1,5 @@ // Visualize plugin styles @import 'np_ready/index'; + +// should be removed while moving the visualize into NP +@import '../../../../../plugins/vis_default_editor/public/index' diff --git a/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts b/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts index addc608efd57d..d5440c4677d8a 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/kibana_services.ts @@ -36,7 +36,7 @@ import { VisualizationsStart } from '../../../../../plugins/visualizations/publi import { SavedVisualizations } from './np_ready/types'; import { UsageCollectionSetup } from '../../../../../plugins/usage_collection/public'; import { KibanaLegacyStart } from '../../../../../plugins/kibana_legacy/public'; -import { DefaultEditorController } from '../../../vis_default_editor/public'; +import { DefaultEditorController } from '../../../../../plugins/vis_default_editor/public'; export interface VisualizeKibanaServices { pluginInitializerContext: PluginInitializerContext; diff --git a/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js b/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js index 7c9ab32ab2f72..a710d3e318749 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js +++ b/src/legacy/core_plugins/kibana/public/visualize/np_ready/legacy_app.js @@ -71,6 +71,7 @@ const getResolvedResults = deps => { return createSavedSearchesLoader({ savedObjectsClient: core.savedObjects.client, indexPatterns: data.indexPatterns, + search: data.search, chrome: core.chrome, overlays: core.overlays, }).get(results.vis.data.savedSearchId); diff --git a/src/legacy/core_plugins/kibana/public/visualize/plugin.ts b/src/legacy/core_plugins/kibana/public/visualize/plugin.ts index 81d8458010f19..4ffbc307c69a8 100644 --- a/src/legacy/core_plugins/kibana/public/visualize/plugin.ts +++ b/src/legacy/core_plugins/kibana/public/visualize/plugin.ts @@ -51,7 +51,7 @@ import { HomePublicPluginSetup, } from '../../../../../plugins/home/public'; import { UsageCollectionSetup } from '../../../../../plugins/usage_collection/public'; -import { DefaultEditorController } from '../../../vis_default_editor/public'; +import { DefaultEditorController } from '../../../../../plugins/vis_default_editor/public'; export interface VisualizePluginStartDependencies { data: DataPublicPluginStart; diff --git a/src/legacy/core_plugins/management/index.ts b/src/legacy/core_plugins/management/index.ts deleted file mode 100644 index 4962c948f842f..0000000000000 --- a/src/legacy/core_plugins/management/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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. - */ - -import { resolve } from 'path'; -import { Legacy } from '../../../../kibana'; - -// eslint-disable-next-line import/no-default-export -export default function ManagementPlugin(kibana: any) { - const config: Legacy.PluginSpecOptions = { - id: 'stack-management', - publicDir: resolve(__dirname, 'public'), - config: (Joi: any) => { - return Joi.object({ - enabled: Joi.boolean().default(true), - }).default(); - }, - init: (server: Legacy.Server) => ({}), - }; - - return new kibana.Plugin(config); -} diff --git a/src/legacy/core_plugins/management/package.json b/src/legacy/core_plugins/management/package.json deleted file mode 100644 index 77d33a7bce3b6..0000000000000 --- a/src/legacy/core_plugins/management/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "management", - "version": "kibana" -} - \ No newline at end of file diff --git a/src/legacy/core_plugins/management/public/legacy.ts b/src/legacy/core_plugins/management/public/legacy.ts deleted file mode 100644 index 96d2c74398a0e..0000000000000 --- a/src/legacy/core_plugins/management/public/legacy.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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. - */ - -/** - * New Platform Shim - * - * In this file, we import any legacy dependencies we have, and shim them into - * our plugin by manually constructing the values that the new platform will - * eventually be passing to the `setup/start` method of our plugin definition. - * - * The idea is that our `plugin.ts` can stay "pure" and not contain any legacy - * world code. Then when it comes time to migrate to the new platform, we can - * simply delete this shim file. - * - * We are also calling `setup/start` here and exporting our public contract so that - * other legacy plugins are able to import from '../core_plugins/management/legacy' - * and receive the response value of the `setup/start` contract, mimicking the - * data that will eventually be injected by the new platform. - */ - -import { PluginInitializerContext } from 'src/core/public'; -import { npSetup, npStart } from 'ui/new_platform'; - -import { plugin } from '.'; - -const pluginInstance = plugin({} as PluginInitializerContext); - -export const setup = pluginInstance.setup(npSetup.core, { home: npSetup.plugins.home }); -export const start = pluginInstance.start(npStart.core, {}); diff --git a/src/legacy/core_plugins/region_map/public/components/region_map_options.tsx b/src/legacy/core_plugins/region_map/public/components/region_map_options.tsx index 4219e2e715150..61cfbf00ded9e 100644 --- a/src/legacy/core_plugins/region_map/public/components/region_map_options.tsx +++ b/src/legacy/core_plugins/region_map/public/components/region_map_options.tsx @@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { FileLayerField, VectorLayer, ServiceSettings } from 'ui/vis/map/service_settings'; -import { VisOptionsProps } from 'src/legacy/core_plugins/vis_default_editor/public'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { NumberInputOption, SelectOption, SwitchOption } from '../../../vis_type_vislib/public'; import { WmsOptions } from '../../../tile_map/public/components/wms_options'; import { RegionMapVisParams } from '../types'; diff --git a/src/legacy/core_plugins/region_map/public/region_map_type.js b/src/legacy/core_plugins/region_map/public/region_map_type.js index 4faa3f92abb5a..9174b03cf843c 100644 --- a/src/legacy/core_plugins/region_map/public/region_map_type.js +++ b/src/legacy/core_plugins/region_map/public/region_map_type.js @@ -22,7 +22,7 @@ import { mapToLayerWithId } from './util'; import { createRegionMapVisualization } from './region_map_visualization'; import { RegionMapOptions } from './components/region_map_options'; import { truncatedColorSchemas } from '../../../../plugins/charts/public'; -import { Schemas } from '../../vis_default_editor/public'; +import { Schemas } from '../../../../plugins/vis_default_editor/public'; // TODO: reference to TILE_MAP plugin should be removed import { ORIGIN } from '../../tile_map/common/origin'; diff --git a/src/legacy/core_plugins/region_map/public/region_map_visualization.js b/src/legacy/core_plugins/region_map/public/region_map_visualization.js index 25641ea76809d..72f9d66e7d2bf 100644 --- a/src/legacy/core_plugins/region_map/public/region_map_visualization.js +++ b/src/legacy/core_plugins/region_map/public/region_map_visualization.js @@ -164,7 +164,8 @@ export function createRegionMapVisualization({ serviceSettings, $injector, uiSet } this._choroplethLayer.on('select', event => { - const rowIndex = this._chartData.rows.findIndex(row => row[0] === event); + const { rows, columns } = this._chartData; + const rowIndex = rows.findIndex(row => row[columns[0].id] === event); this._vis.API.events.filter({ table: this._chartData, column: 0, diff --git a/src/legacy/core_plugins/tile_map/public/components/tile_map_options.tsx b/src/legacy/core_plugins/tile_map/public/components/tile_map_options.tsx index 168f56b771b7e..9169647aa2aef 100644 --- a/src/legacy/core_plugins/tile_map/public/components/tile_map_options.tsx +++ b/src/legacy/core_plugins/tile_map/public/components/tile_map_options.tsx @@ -21,7 +21,7 @@ import React, { useEffect } from 'react'; import { EuiPanel, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { VisOptionsProps } from 'src/legacy/core_plugins/vis_default_editor/public'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { BasicOptions, RangeOption, diff --git a/src/legacy/core_plugins/tile_map/public/tile_map_type.js b/src/legacy/core_plugins/tile_map/public/tile_map_type.js index 39d39a4c8f8fc..fe82ad5c7352b 100644 --- a/src/legacy/core_plugins/tile_map/public/tile_map_type.js +++ b/src/legacy/core_plugins/tile_map/public/tile_map_type.js @@ -22,7 +22,7 @@ import { i18n } from '@kbn/i18n'; import { convertToGeoJson } from 'ui/vis/map/convert_to_geojson'; -import { Schemas } from '../../vis_default_editor/public'; +import { Schemas } from '../../../../plugins/vis_default_editor/public'; import { createTileMapVisualization } from './tile_map_visualization'; import { TileMapOptions } from './components/tile_map_options'; import { MapTypes } from './map_types'; diff --git a/src/legacy/core_plugins/timelion/public/services/saved_sheets.ts b/src/legacy/core_plugins/timelion/public/services/saved_sheets.ts index 201b21f932988..e7f431a178ea0 100644 --- a/src/legacy/core_plugins/timelion/public/services/saved_sheets.ts +++ b/src/legacy/core_plugins/timelion/public/services/saved_sheets.ts @@ -28,6 +28,7 @@ const savedObjectsClient = npStart.core.savedObjects.client; const services = { savedObjectsClient, indexPatterns: npStart.plugins.data.indexPatterns, + search: npStart.plugins.data.search, chrome: npStart.core.chrome, overlays: npStart.core.overlays, }; diff --git a/src/legacy/core_plugins/vis_default_editor/index.ts b/src/legacy/core_plugins/vis_default_editor/index.ts deleted file mode 100644 index ee7b5ee4a62ff..0000000000000 --- a/src/legacy/core_plugins/vis_default_editor/index.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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. - */ - -import { resolve } from 'path'; -import { Legacy } from 'kibana'; - -import { LegacyPluginApi, LegacyPluginInitializer } from '../../../../src/legacy/types'; - -const vidDefaultEditorPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPluginApi) => - new Plugin({ - id: 'vis_default_editor', - require: [], - publicDir: resolve(__dirname, 'public'), - uiExports: { - styleSheetPaths: resolve(__dirname, 'public/index.scss'), - }, - init: (server: Legacy.Server) => ({}), - config(Joi: any) { - return Joi.object({ - enabled: Joi.boolean().default(true), - }).default(); - }, - } as Legacy.PluginSpecOptions); - -// eslint-disable-next-line import/no-default-export -export default vidDefaultEditorPluginInitializer; diff --git a/src/legacy/core_plugins/vis_default_editor/package.json b/src/legacy/core_plugins/vis_default_editor/package.json deleted file mode 100644 index 77dcaff41da6b..0000000000000 --- a/src/legacy/core_plugins/vis_default_editor/package.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "vis_default_editor", - "version": "kibana" -} diff --git a/src/legacy/core_plugins/vis_type_markdown/public/markdown_options.tsx b/src/legacy/core_plugins/vis_type_markdown/public/markdown_options.tsx index 8a4297d3b8149..a6349793619a0 100644 --- a/src/legacy/core_plugins/vis_type_markdown/public/markdown_options.tsx +++ b/src/legacy/core_plugins/vis_type_markdown/public/markdown_options.tsx @@ -30,7 +30,7 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from 'src/legacy/core_plugins/vis_default_editor/public'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { MarkdownVisParams } from './types'; function MarkdownOptions({ stateParams, setValue }: VisOptionsProps) { diff --git a/src/legacy/core_plugins/vis_type_markdown/public/markdown_vis.ts b/src/legacy/core_plugins/vis_type_markdown/public/markdown_vis.ts index b84d9638eb973..57ea6d9c9bb3d 100644 --- a/src/legacy/core_plugins/vis_type_markdown/public/markdown_vis.ts +++ b/src/legacy/core_plugins/vis_type_markdown/public/markdown_vis.ts @@ -22,7 +22,7 @@ import { i18n } from '@kbn/i18n'; import { MarkdownVisWrapper } from './markdown_vis_controller'; import { MarkdownOptions } from './markdown_options'; import { SettingsOptions } from './settings_options'; -import { DefaultEditorSize } from '../../vis_default_editor/public'; +import { DefaultEditorSize } from '../../../../plugins/vis_default_editor/public'; export const markdownVisDefinition = { name: 'markdown', diff --git a/src/legacy/core_plugins/vis_type_markdown/public/settings_options.tsx b/src/legacy/core_plugins/vis_type_markdown/public/settings_options.tsx index ac1d4bcc82cec..552fd63373554 100644 --- a/src/legacy/core_plugins/vis_type_markdown/public/settings_options.tsx +++ b/src/legacy/core_plugins/vis_type_markdown/public/settings_options.tsx @@ -21,7 +21,7 @@ import React from 'react'; import { EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { VisOptionsProps } from 'src/legacy/core_plugins/vis_default_editor/public'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { RangeOption, SwitchOption } from '../../vis_type_vislib/public'; import { MarkdownVisParams } from './types'; diff --git a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_options.tsx b/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_options.tsx index 661f16d6497ba..5c3032511f09a 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_options.tsx +++ b/src/legacy/core_plugins/vis_type_metric/public/components/metric_vis_options.tsx @@ -29,7 +29,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from 'src/legacy/core_plugins/vis_default_editor/public'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { ColorModes, ColorRanges, diff --git a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_fn.test.ts b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_fn.test.ts index 22c32895d6803..4094cd4eff060 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_fn.test.ts +++ b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_fn.test.ts @@ -23,10 +23,6 @@ import { functionWrapper } from '../../../../plugins/expressions/common/expressi jest.mock('ui/new_platform'); -jest.mock('../../vis_default_editor/public', () => ({ - Schemas: class {}, -})); - describe('interpreter/functions#metric', () => { const fn = functionWrapper(createMetricVisFn()); const context = { diff --git a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts index 459da47556307..706693eff1007 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts +++ b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.test.ts @@ -22,10 +22,6 @@ import { MetricVisComponent } from './components/metric_vis_component'; jest.mock('ui/new_platform'); -jest.mock('../../vis_default_editor/public', () => ({ - Schemas: class {}, -})); - describe('metric_vis - createMetricVisTypeDefinition', () => { it('has metric vis component set', () => { const def = createMetricVisTypeDefinition(); diff --git a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.ts b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.ts index f29164f7e540d..3bbb8964122e5 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.ts +++ b/src/legacy/core_plugins/vis_type_metric/public/metric_vis_type.ts @@ -24,7 +24,7 @@ import { MetricVisOptions } from './components/metric_vis_options'; import { ColorModes } from '../../vis_type_vislib/public'; import { ColorSchemas, colorSchemas } from '../../../../plugins/charts/public'; import { AggGroupNames } from '../../../../plugins/data/public'; -import { Schemas } from '../../vis_default_editor/public'; +import { Schemas } from '../../../../plugins/vis_default_editor/public'; export const createMetricVisTypeDefinition = () => ({ name: 'metric', diff --git a/src/legacy/core_plugins/vis_type_metric/public/services.ts b/src/legacy/core_plugins/vis_type_metric/public/services.ts index 5af11bc7f0b03..b303ccd5aeed2 100644 --- a/src/legacy/core_plugins/vis_type_metric/public/services.ts +++ b/src/legacy/core_plugins/vis_type_metric/public/services.ts @@ -17,7 +17,7 @@ * under the License. */ -import { createGetterSetter } from '../../../../plugins/kibana_utils/common'; +import { createGetterSetter } from '../../../../plugins/kibana_utils/public'; import { DataPublicPluginStart } from '../../../../plugins/data/public'; export const [getFormatService, setFormatService] = createGetterSetter< diff --git a/src/legacy/core_plugins/vis_type_table/public/components/table_vis_options.tsx b/src/legacy/core_plugins/vis_type_table/public/components/table_vis_options.tsx index 30a9526273166..d01ab31e0a843 100644 --- a/src/legacy/core_plugins/vis_type_table/public/components/table_vis_options.tsx +++ b/src/legacy/core_plugins/vis_type_table/public/components/table_vis_options.tsx @@ -23,7 +23,7 @@ import { EuiIconTip, EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from 'src/legacy/core_plugins/vis_default_editor/public'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { search } from '../../../../../plugins/data/public'; import { NumberInputOption, SwitchOption, SelectOption } from '../../../vis_type_vislib/public'; import { TableVisParams } from '../types'; diff --git a/src/legacy/core_plugins/vis_type_table/public/services.ts b/src/legacy/core_plugins/vis_type_table/public/services.ts index 08efed733cafe..b4b491ac7a555 100644 --- a/src/legacy/core_plugins/vis_type_table/public/services.ts +++ b/src/legacy/core_plugins/vis_type_table/public/services.ts @@ -17,7 +17,7 @@ * under the License. */ -import { createGetterSetter } from '../../../../plugins/kibana_utils/common'; +import { createGetterSetter } from '../../../../plugins/kibana_utils/public'; import { DataPublicPluginStart } from '../../../../plugins/data/public'; export const [getFormatService, setFormatService] = createGetterSetter< diff --git a/src/legacy/core_plugins/vis_type_table/public/table_vis_type.ts b/src/legacy/core_plugins/vis_type_table/public/table_vis_type.ts index d26e860e51272..43816121bc23b 100644 --- a/src/legacy/core_plugins/vis_type_table/public/table_vis_type.ts +++ b/src/legacy/core_plugins/vis_type_table/public/table_vis_type.ts @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; import { AggGroupNames } from '../../../../plugins/data/public'; -import { Schemas } from '../../vis_default_editor/public'; +import { Schemas } from '../../../../plugins/vis_default_editor/public'; import { Vis } from '../../../../plugins/visualizations/public'; import { tableVisResponseHandler } from './table_vis_response_handler'; // @ts-ignore diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx b/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx index a9e816f70cf53..80e4e1de7ddab 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx +++ b/src/legacy/core_plugins/vis_type_tagcloud/public/components/tag_cloud_options.tsx @@ -20,8 +20,8 @@ import React from 'react'; import { EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { ValidatedDualRange } from '../../../../../../src/plugins/kibana_react/public'; -import { VisOptionsProps } from '../../../vis_default_editor/public'; import { SelectOption, SwitchOption } from '../../../vis_type_vislib/public'; import { TagCloudVisParams } from '../types'; diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/services.ts b/src/legacy/core_plugins/vis_type_tagcloud/public/services.ts index fef46282eb8dd..272bed3e91a08 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/services.ts +++ b/src/legacy/core_plugins/vis_type_tagcloud/public/services.ts @@ -17,7 +17,7 @@ * under the License. */ -import { createGetterSetter } from '../../../../plugins/kibana_utils/common'; +import { createGetterSetter } from '../../../../plugins/kibana_utils/public'; import { DataPublicPluginStart } from '../../../../plugins/data/public'; export const [getFormatService, setFormatService] = createGetterSetter< diff --git a/src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_type.ts b/src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_type.ts index 5a8cc3004a315..b7dfa62c93fb9 100644 --- a/src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_type.ts +++ b/src/legacy/core_plugins/vis_type_tagcloud/public/tag_cloud_type.ts @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; -import { Schemas } from '../../vis_default_editor/public'; +import { Schemas } from '../../../../plugins/vis_default_editor/public'; import { TagCloudOptions } from './components/tag_cloud_options'; diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_interval.tsx b/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_interval.tsx index 6e29b111d422a..8a8e1b22fb78d 100644 --- a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_interval.tsx +++ b/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_interval.tsx @@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n'; import { search } from '../../../../../plugins/data/public'; const { isValidEsInterval } = search.aggs; -import { useValidation } from '../../../vis_default_editor/public'; +import { useValidation } from '../../../../../plugins/vis_default_editor/public'; const intervalOptions = [ { diff --git a/src/legacy/core_plugins/vis_type_timelion/public/timelion_options.tsx b/src/legacy/core_plugins/vis_type_timelion/public/timelion_options.tsx index b7c40e15c11fd..afffcf7ccaf7a 100644 --- a/src/legacy/core_plugins/vis_type_timelion/public/timelion_options.tsx +++ b/src/legacy/core_plugins/vis_type_timelion/public/timelion_options.tsx @@ -20,9 +20,9 @@ import React, { useCallback } from 'react'; import { EuiPanel } from '@elastic/eui'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { VisParams } from './timelion_vis_fn'; import { TimelionInterval, TimelionExpressionInput } from './components'; -import { VisOptionsProps } from '../../vis_default_editor/public'; function TimelionOptions({ stateParams, setValue, setValidity }: VisOptionsProps) { const setInterval = useCallback((value: VisParams['interval']) => setValue('interval', value), [ diff --git a/src/legacy/core_plugins/vis_type_timelion/public/timelion_vis_type.tsx b/src/legacy/core_plugins/vis_type_timelion/public/timelion_vis_type.tsx index 6679553004097..5be77b3e51a6a 100644 --- a/src/legacy/core_plugins/vis_type_timelion/public/timelion_vis_type.tsx +++ b/src/legacy/core_plugins/vis_type_timelion/public/timelion_vis_type.tsx @@ -21,7 +21,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { KibanaContextProvider } from '../../../../plugins/kibana_react/public'; -import { DefaultEditorSize } from '../../vis_default_editor/public'; +import { DefaultEditorSize } from '../../../../plugins/vis_default_editor/public'; import { getTimelionRequestHandler } from './helpers/timelion_request_handler'; import { TimelionVisComponent, TimelionVisComponentProp } from './components'; import { TimelionOptions } from './timelion_options'; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/metrics_type.ts b/src/legacy/core_plugins/vis_type_timeseries/public/metrics_type.ts index 30c62d778933b..1db35c406eb13 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/metrics_type.ts +++ b/src/legacy/core_plugins/vis_type_timeseries/public/metrics_type.ts @@ -25,7 +25,7 @@ import { metricsRequestHandler } from './request_handler'; import { EditorController } from './editor_controller'; // @ts-ignore import { PANEL_TYPES } from '../../../../plugins/vis_type_timeseries/common/panel_types'; -import { defaultFeedbackMessage } from '../../../../plugins/kibana_utils/common'; +import { defaultFeedbackMessage } from '../../../../plugins/kibana_utils/public'; export const metricsVisDefinition = { name: 'metrics', diff --git a/src/legacy/core_plugins/vis_type_vega/public/__mocks__/services.ts b/src/legacy/core_plugins/vis_type_vega/public/__mocks__/services.ts index 64a9aaaf3b7a6..b2f3e5b2241e6 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/__mocks__/services.ts +++ b/src/legacy/core_plugins/vis_type_vega/public/__mocks__/services.ts @@ -17,7 +17,7 @@ * under the License. */ -import { createGetterSetter } from '../../../../../plugins/kibana_utils/common'; +import { createGetterSetter } from '../../../../../plugins/kibana_utils/public'; import { DataPublicPluginStart } from '../../../../../plugins/data/public'; import { IUiSettingsClient, NotificationsStart, SavedObjectsStart } from 'kibana/public'; import { dataPluginMock } from '../../../../../plugins/data/public/mocks'; diff --git a/src/legacy/core_plugins/vis_type_vega/public/components/vega_vis_editor.tsx b/src/legacy/core_plugins/vis_type_vega/public/components/vega_vis_editor.tsx index 707a6830b5ba4..31144065d7b40 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/components/vega_vis_editor.tsx +++ b/src/legacy/core_plugins/vis_type_vega/public/components/vega_vis_editor.tsx @@ -24,11 +24,11 @@ import compactStringify from 'json-stringify-pretty-compact'; import hjson from 'hjson'; import { i18n } from '@kbn/i18n'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { getNotifications } from '../services'; import { VisParams } from '../vega_fn'; import { VegaHelpMenu } from './vega_help_menu'; import { VegaActionsMenu } from './vega_actions_menu'; -import { VisOptionsProps } from '../../../vis_default_editor/public'; const aceOptions = { maxLines: Infinity, diff --git a/src/legacy/core_plugins/vis_type_vega/public/vega_type.ts b/src/legacy/core_plugins/vis_type_vega/public/vega_type.ts index b0ec90d2c378f..f56d7682efc6f 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/vega_type.ts +++ b/src/legacy/core_plugins/vis_type_vega/public/vega_type.ts @@ -18,11 +18,10 @@ */ import { i18n } from '@kbn/i18n'; -// @ts-ignore -import { DefaultEditorSize } from '../../vis_default_editor/public'; +import { DefaultEditorSize } from '../../../../plugins/vis_default_editor/public'; import { VegaVisualizationDependencies } from './plugin'; import { VegaVisEditor } from './components'; -import { defaultFeedbackMessage } from '../../../../plugins/kibana_utils/common'; +import { defaultFeedbackMessage } from '../../../../plugins/kibana_utils/public'; import { createVegaRequestHandler } from './vega_request_handler'; // @ts-ignore diff --git a/src/legacy/core_plugins/vis_type_vislib/public/area.ts b/src/legacy/core_plugins/vis_type_vislib/public/area.ts index e79555470298b..68decacaaa040 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/area.ts +++ b/src/legacy/core_plugins/vis_type_vislib/public/area.ts @@ -24,7 +24,7 @@ import { palettes } from '@elastic/eui/lib/services'; import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; import { AggGroupNames } from '../../../../plugins/data/public'; -import { Schemas } from '../../vis_default_editor/public'; +import { Schemas } from '../../../../plugins/vis_default_editor/public'; import { Positions, ChartTypes, diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/common/basic_options.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/common/basic_options.tsx index 1138f66d21cfa..baf3e8ecd1b28 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/common/basic_options.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/common/basic_options.tsx @@ -20,7 +20,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { VisOptionsProps } from '../../../../vis_default_editor/public'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { SwitchOption } from './switch'; import { SelectOption } from './select'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/common/color_ranges.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/common/color_ranges.tsx index 2c9b1b543e8c2..84c70f10b12da 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/common/color_ranges.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/common/color_ranges.tsx @@ -22,7 +22,10 @@ import { last } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { RangeValues, RangesParamEditor } from '../../../../vis_default_editor/public'; +import { + RangeValues, + RangesParamEditor, +} from '../../../../../../plugins/vis_default_editor/public'; export type SetColorRangeValue = (paramName: string, value: RangeValues[]) => void; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/common/color_schema.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/common/color_schema.tsx index 06ce0a2b4af64..35d7f7b13235e 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/common/color_schema.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/common/color_schema.tsx @@ -22,7 +22,7 @@ import { i18n } from '@kbn/i18n'; import { EuiLink, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from '../../../../vis_default_editor/public'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { SelectOption } from './select'; import { SwitchOption } from './switch'; import { ColorSchemaVislibParams } from '../../types'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/common/validation_wrapper.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/common/validation_wrapper.tsx index c069d4c935669..718056fd85492 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/common/validation_wrapper.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/common/validation_wrapper.tsx @@ -19,7 +19,7 @@ import React, { useEffect, useState, useCallback } from 'react'; -import { VisOptionsProps } from '../../../../vis_default_editor/public'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; export interface ValidationVisOptionsProps extends VisOptionsProps { setMultipleValidity(paramName: string, isValid: boolean): void; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/index.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/index.tsx index 706035a7b814e..6109b548f9412 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/index.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/gauge/index.tsx @@ -20,7 +20,7 @@ import React, { useCallback } from 'react'; import { EuiSpacer } from '@elastic/eui'; -import { VisOptionsProps } from '../../../../../vis_default_editor/public'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { GaugeVisParams } from '../../../gauge'; import { RangesPanel } from './ranges_panel'; import { StylePanel } from './style_panel'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/heatmap/index.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/heatmap/index.tsx index 452b9ed9bdbb1..715b5902b69da 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/heatmap/index.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/heatmap/index.tsx @@ -23,7 +23,7 @@ import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from '../../../../../vis_default_editor/public'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { BasicOptions, ColorRanges, diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/heatmap/labels_panel.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/heatmap/labels_panel.tsx index c74f0ef765c8d..38811bd836459 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/heatmap/labels_panel.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/heatmap/labels_panel.tsx @@ -23,7 +23,7 @@ import { EuiColorPicker, EuiFormRow, EuiPanel, EuiSpacer, EuiTitle } from '@elas import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from '../../../../../vis_default_editor/public'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { ValueAxis } from '../../../types'; import { HeatmapVisParams } from '../../../heatmap'; import { SwitchOption } from '../../common'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/index.test.tsx.snap b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/index.test.tsx.snap index 442bc826d51fc..09e0753d592e5 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/index.test.tsx.snap +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/__snapshots__/index.test.tsx.snap @@ -54,6 +54,7 @@ exports[`MetricsAxisOptions component should init with the default set of props } vis={ Object { + "serialize": [MockFunction], "setState": [MockFunction], "type": Object { "schemas": Object { @@ -126,6 +127,7 @@ exports[`MetricsAxisOptions component should init with the default set of props } vis={ Object { + "serialize": [MockFunction], "setState": [MockFunction], "type": Object { "schemas": Object { @@ -169,6 +171,7 @@ exports[`MetricsAxisOptions component should init with the default set of props setCategoryAxis={[Function]} vis={ Object { + "serialize": [MockFunction], "setState": [MockFunction], "type": Object { "schemas": Object { diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.tsx index 049df0cdd77be..915885388640c 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/category_axis_panel.tsx @@ -23,7 +23,7 @@ import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from 'src/legacy/core_plugins/vis_default_editor/public'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { Axis } from '../../../types'; import { SelectOption, SwitchOption } from '../../common'; import { LabelOptions, SetAxisLabel } from './label_options'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.test.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.test.tsx index a3f150e718817..524792d1460fe 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.test.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.test.tsx @@ -95,6 +95,7 @@ describe('MetricsAxisOptions component', () => { schemas: { metrics: [{ name: 'metric' }] }, }, setState: jest.fn(), + serialize: jest.fn(), }, stateParams: { valueAxes: [axis], diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.tsx index c7b4562b1087e..114305d653dd1 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/metrics_axes/index.tsx @@ -299,7 +299,7 @@ function MetricsAxisOptions(props: ValidationVisOptionsProps) }, [stateParams.seriesParams]); useEffect(() => { - vis.setState({ type: visType } as any); + vis.setState({ ...vis.serialize(), type: visType }); }, [vis, visType]); return isTabSelected ? ( diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/pie.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/pie.tsx index 2182edafb3ebf..4c0be456aad64 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/pie.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/pie.tsx @@ -22,7 +22,7 @@ import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from '../../../../vis_default_editor/public'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { BasicOptions, TruncateLabelsOption, SwitchOption } from '../common'; import { PieVisParams } from '../../pie'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/grid_panel.tsx b/src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/grid_panel.tsx index db9acafac305c..bb2b3f8fddb49 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/grid_panel.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/components/options/point_series/grid_panel.tsx @@ -22,7 +22,7 @@ import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { VisOptionsProps } from '../../../../../vis_default_editor/public'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { SelectOption, SwitchOption } from '../../common'; import { BasicVislibParams, ValueAxis } from '../../../types'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/gauge.ts b/src/legacy/core_plugins/vis_type_vislib/public/gauge.ts index 4610bd37db5f1..7ad821dbf2f30 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/gauge.ts +++ b/src/legacy/core_plugins/vis_type_vislib/public/gauge.ts @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; -import { RangeValues, Schemas } from '../../vis_default_editor/public'; +import { RangeValues, Schemas } from '../../../../plugins/vis_default_editor/public'; import { AggGroupNames } from '../../../../plugins/data/public'; import { GaugeOptions } from './components/options'; import { getGaugeCollections, Alignments, ColorModes, GaugeTypes } from './utils/collections'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/goal.ts b/src/legacy/core_plugins/vis_type_vislib/public/goal.ts index c918128d01f11..6c311bebe0717 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/goal.ts +++ b/src/legacy/core_plugins/vis_type_vislib/public/goal.ts @@ -25,7 +25,7 @@ import { createVislibVisController } from './vis_controller'; import { VisTypeVislibDependencies } from './plugin'; import { ColorSchemas } from '../../../../plugins/charts/public'; import { AggGroupNames } from '../../../../plugins/data/public'; -import { Schemas } from '../../vis_default_editor/public'; +import { Schemas } from '../../../../plugins/vis_default_editor/public'; export const createGoalVisTypeDefinition = (deps: VisTypeVislibDependencies) => ({ name: 'goal', diff --git a/src/legacy/core_plugins/vis_type_vislib/public/heatmap.ts b/src/legacy/core_plugins/vis_type_vislib/public/heatmap.ts index 39a583f3c9641..88b4f0fcaf87e 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/heatmap.ts +++ b/src/legacy/core_plugins/vis_type_vislib/public/heatmap.ts @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; -import { RangeValues, Schemas } from '../../vis_default_editor/public'; +import { RangeValues, Schemas } from '../../../../plugins/vis_default_editor/public'; import { AggGroupNames } from '../../../../plugins/data/public'; import { AxisTypes, getHeatmapCollections, Positions, ScaleTypes } from './utils/collections'; import { HeatmapOptions } from './components/options'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/histogram.ts b/src/legacy/core_plugins/vis_type_vislib/public/histogram.ts index 15ef369e5150e..54ec8f98203e2 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/histogram.ts +++ b/src/legacy/core_plugins/vis_type_vislib/public/histogram.ts @@ -24,7 +24,7 @@ import { palettes } from '@elastic/eui/lib/services'; import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; import { AggGroupNames } from '../../../../plugins/data/public'; -import { Schemas } from '../../vis_default_editor/public'; +import { Schemas } from '../../../../plugins/vis_default_editor/public'; import { Positions, ChartTypes, diff --git a/src/legacy/core_plugins/vis_type_vislib/public/horizontal_bar.ts b/src/legacy/core_plugins/vis_type_vislib/public/horizontal_bar.ts index 8b5811628855c..dc47252ccd44f 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/horizontal_bar.ts +++ b/src/legacy/core_plugins/vis_type_vislib/public/horizontal_bar.ts @@ -24,7 +24,7 @@ import { palettes } from '@elastic/eui/lib/services'; import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; import { AggGroupNames } from '../../../../plugins/data/public'; -import { Schemas } from '../../vis_default_editor/public'; +import { Schemas } from '../../../../plugins/vis_default_editor/public'; import { Positions, ChartTypes, diff --git a/src/legacy/core_plugins/vis_type_vislib/public/line.ts b/src/legacy/core_plugins/vis_type_vislib/public/line.ts index ac4cda869fe29..885ab295d11e1 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/line.ts +++ b/src/legacy/core_plugins/vis_type_vislib/public/line.ts @@ -24,7 +24,7 @@ import { palettes } from '@elastic/eui/lib/services'; import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; import { AggGroupNames } from '../../../../plugins/data/public'; -import { Schemas } from '../../vis_default_editor/public'; +import { Schemas } from '../../../../plugins/vis_default_editor/public'; import { Positions, ChartTypes, diff --git a/src/legacy/core_plugins/vis_type_vislib/public/pie.ts b/src/legacy/core_plugins/vis_type_vislib/public/pie.ts index 0f1bd93f5b5bd..2774836baa381 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/pie.ts +++ b/src/legacy/core_plugins/vis_type_vislib/public/pie.ts @@ -20,7 +20,7 @@ import { i18n } from '@kbn/i18n'; import { AggGroupNames } from '../../../../plugins/data/public'; -import { Schemas } from '../../vis_default_editor/public'; +import { Schemas } from '../../../../plugins/vis_default_editor/public'; import { PieOptions } from './components/options'; import { getPositions, Positions } from './utils/collections'; import { createVislibVisController } from './vis_controller'; diff --git a/src/legacy/core_plugins/vis_type_vislib/public/services.ts b/src/legacy/core_plugins/vis_type_vislib/public/services.ts index da50e227d84d2..0d6b1b5e8de58 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/services.ts +++ b/src/legacy/core_plugins/vis_type_vislib/public/services.ts @@ -17,7 +17,7 @@ * under the License. */ -import { createGetterSetter } from '../../../../plugins/kibana_utils/common'; +import { createGetterSetter } from '../../../../plugins/kibana_utils/public'; import { DataPublicPluginStart } from '../../../../plugins/data/public'; export const [getDataActions, setDataActions] = createGetterSetter< diff --git a/src/legacy/core_plugins/vis_type_vislib/public/utils/common_config.tsx b/src/legacy/core_plugins/vis_type_vislib/public/utils/common_config.tsx index 6da40686a8b50..de867dc72bba7 100644 --- a/src/legacy/core_plugins/vis_type_vislib/public/utils/common_config.tsx +++ b/src/legacy/core_plugins/vis_type_vislib/public/utils/common_config.tsx @@ -20,7 +20,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { VisOptionsProps } from '../../../vis_default_editor/public'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; import { PointSeriesOptions, MetricsAxisOptions } from '../components/options'; import { ValidationWrapper } from '../components/common'; import { BasicVislibParams } from '../types'; diff --git a/src/legacy/server/status/collectors/get_ops_stats_collector.js b/src/legacy/server/status/collectors/get_ops_stats_collector.js deleted file mode 100644 index b733e2e721e3a..0000000000000 --- a/src/legacy/server/status/collectors/get_ops_stats_collector.js +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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. - */ - -import { KIBANA_STATS_TYPE } from '../constants'; -import { getKibanaInfoForStats } from '../lib'; - -/* - * Initialize a collector for Kibana Ops Stats - * - * NOTE this collector's fetch method returns the latest stats from the - * Hapi/Good/Even-Better ops event listener. Therefore, the stats reset - * every 5 seconds (the default value of the ops.interval configuration - * setting). That makes it geared for providing the latest "real-time" - * stats. In the long-term, fetch should return stats that constantly - * accumulate over the server's uptime for better machine readability. - * Since the data is captured, timestamped and stored, the historical - * data can provide "real-time" stats by calculating a derivative of - * the metrics. - * See PR comment in https://github.com/elastic/kibana/pull/20577/files#r202416647 - */ -export function getOpsStatsCollector(usageCollection, server, kbnServer) { - return usageCollection.makeStatsCollector({ - type: KIBANA_STATS_TYPE, - fetch: () => { - return { - kibana: getKibanaInfoForStats(server, kbnServer), - ...kbnServer.metrics, // latest metrics captured from the ops event listener in src/legacy/server/status/index - }; - }, - isReady: () => true, - ignoreForInternalUploader: true, // Ignore this one from internal uploader. A different stats collector is used there. - }); -} - -export function registerOpsStatsCollector(usageCollection, server, kbnServer) { - if (usageCollection) { - const collector = getOpsStatsCollector(usageCollection, server, kbnServer); - usageCollection.registerCollector(collector); - } -} diff --git a/src/legacy/server/status/index.js b/src/legacy/server/status/index.js index df02b3c45ec2f..5bd1efa99eb2c 100644 --- a/src/legacy/server/status/index.js +++ b/src/legacy/server/status/index.js @@ -20,7 +20,6 @@ import ServerStatus from './server_status'; import { Metrics } from './lib/metrics'; import { registerStatusPage, registerStatusApi, registerStatsApi } from './routes'; -import { registerOpsStatsCollector } from './collectors'; import Oppsy from 'oppsy'; import { cloneDeep } from 'lodash'; import { getOSInfo } from './lib/get_os_info'; @@ -28,7 +27,6 @@ import { getOSInfo } from './lib/get_os_info'; export function statusMixin(kbnServer, server, config) { kbnServer.status = new ServerStatus(kbnServer.server); const { usageCollection } = server.newPlatform.setup.plugins; - registerOpsStatsCollector(usageCollection, server, kbnServer); const metrics = new Metrics(config, server); diff --git a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js index c8bf2d1e88595..9078bfe784aab 100644 --- a/src/legacy/ui/public/new_platform/new_platform.karma_mock.js +++ b/src/legacy/ui/public/new_platform/new_platform.karma_mock.js @@ -290,6 +290,10 @@ export const npSetup = { }), }, }, + indexPatternManagement: { + list: { addListConfig: sinon.fake() }, + creation: { addCreationConfig: sinon.fake() }, + }, discover: { docViews: { addDocView: sinon.fake(), @@ -325,6 +329,17 @@ export const npStart = { }), }, }, + indexPatternManagement: { + list: { + getType: sinon.fake(), + getIndexPatternCreationOptions: sinon.fake(), + }, + creation: { + getIndexPatternTags: sinon.fake(), + getFieldInfo: sinon.fake(), + areScriptedFieldsEnabled: sinon.fake(), + }, + }, embeddable: { getEmbeddableFactory: sinon.fake(), getEmbeddableFactories: sinon.fake(), diff --git a/src/legacy/ui/public/new_platform/new_platform.ts b/src/legacy/ui/public/new_platform/new_platform.ts index b4b5099081759..cdd7e1a994912 100644 --- a/src/legacy/ui/public/new_platform/new_platform.ts +++ b/src/legacy/ui/public/new_platform/new_platform.ts @@ -47,6 +47,10 @@ import { AdvancedSettingsStart, } from '../../../../plugins/advanced_settings/public'; import { ManagementSetup, ManagementStart } from '../../../../plugins/management/public'; +import { + IndexPatternManagementSetup, + IndexPatternManagementStart, +} from '../../../../plugins/index_pattern_management/public'; import { BfetchPublicSetup, BfetchPublicStart } from '../../../../plugins/bfetch/public'; import { UsageCollectionSetup } from '../../../../plugins/usage_collection/public'; import { TelemetryPluginSetup, TelemetryPluginStart } from '../../../../plugins/telemetry/public'; @@ -86,6 +90,7 @@ export interface PluginsSetup { visualizations: VisualizationsSetup; telemetry?: TelemetryPluginSetup; savedObjectsManagement: SavedObjectsManagementPluginSetup; + indexPatternManagement: IndexPatternManagementSetup; } export interface PluginsStart { @@ -107,6 +112,7 @@ export interface PluginsStart { telemetry?: TelemetryPluginStart; dashboard: DashboardStart; savedObjectsManagement: SavedObjectsManagementPluginStart; + indexPatternManagement: IndexPatternManagementStart; } export const npSetup = { diff --git a/src/legacy/ui/public/new_platform/set_services.ts b/src/legacy/ui/public/new_platform/set_services.ts index 8cf015d5dff5c..400f31e73ffa1 100644 --- a/src/legacy/ui/public/new_platform/set_services.ts +++ b/src/legacy/ui/public/new_platform/set_services.ts @@ -72,9 +72,11 @@ export function setStartServices(npStart: NpStart) { visualizationsServices.setAggs(npStart.plugins.data.search.aggs); visualizationsServices.setOverlays(npStart.core.overlays); visualizationsServices.setChrome(npStart.core.chrome); + visualizationsServices.setSearch(npStart.plugins.data.search); const savedVisualizationsLoader = createSavedVisLoader({ savedObjectsClient: npStart.core.savedObjects.client, indexPatterns: npStart.plugins.data.indexPatterns, + search: npStart.plugins.data.search, chrome: npStart.core.chrome, overlays: npStart.core.overlays, visualizationTypes: visualizationsServices.getTypes(), diff --git a/src/legacy/ui/ui_render/bootstrap/template.js.hbs b/src/legacy/ui/ui_render/bootstrap/template.js.hbs index ad4aa97d8ea7a..1093153edbbf7 100644 --- a/src/legacy/ui/ui_render/bootstrap/template.js.hbs +++ b/src/legacy/ui/ui_render/bootstrap/template.js.hbs @@ -30,33 +30,27 @@ if (window.__kbnStrictCsp__ && window.__kbnCspNotEnforced__) { function loadStyleSheet(url, cb) { var dom = document.createElement('link'); + dom.rel = 'stylesheet'; + dom.type = 'text/css'; + dom.href = url; dom.addEventListener('error', failure); - dom.setAttribute('rel', 'stylesheet'); - dom.setAttribute('type', 'text/css'); - dom.setAttribute('href', url); dom.addEventListener('load', cb); document.head.appendChild(dom); } function loadScript(url, cb) { var dom = document.createElement('script'); - dom.setAttribute('async', ''); + {{!-- NOTE: async = false is used to trigger async-download/ordered-execution as outlined here: https://www.html5rocks.com/en/tutorials/speed/script-loading/ --}} + dom.async = false; + dom.src = url; dom.addEventListener('error', failure); - dom.setAttribute('src', url); dom.addEventListener('load', cb); document.head.appendChild(dom); } - function load(urlSet, cb) { - if (urlSet.deps) { - load({ urls: urlSet.deps }, function () { - load({ urls: urlSet.urls }, cb); - }); - return; - } - - var pending = urlSet.urls.length; - urlSet.urls.forEach(function (url) { + function load(urls, cb) { + var pending = urls.length; + urls.forEach(function (url) { var innerCb = function () { pending = pending - 1; if (pending === 0 && typeof cb === 'function') { @@ -74,36 +68,27 @@ if (window.__kbnStrictCsp__ && window.__kbnCspNotEnforced__) { }); } - load({ - deps: [ + load([ {{#each sharedJsDepFilenames}} '{{../regularBundlePath}}/kbn-ui-shared-deps/{{this}}', {{/each}} - ], - urls: [ - { - deps: [ - '{{regularBundlePath}}/kbn-ui-shared-deps/{{sharedJsFilename}}', - { - deps: [ - '{{dllBundlePath}}/vendors_runtime.bundle.dll.js' - ], - urls: [ - {{#each dllJsChunks}} - '{{this}}', - {{/each}} - ] - }, - '{{regularBundlePath}}/commons.bundle.js', - ], - urls: [ - '{{regularBundlePath}}/{{appId}}.bundle.js', - {{#each styleSheetPaths}} - '{{this}}', - {{/each}} - ] - } - ] + '{{regularBundlePath}}/kbn-ui-shared-deps/{{sharedJsFilename}}', + '{{dllBundlePath}}/vendors_runtime.bundle.dll.js', + {{#each dllJsChunks}} + '{{this}}', + {{/each}} + '{{regularBundlePath}}/commons.bundle.js', + {{!-- '{{regularBundlePath}}/plugin/data/data.plugin.js', --}} + '{{regularBundlePath}}/plugin/kibanaUtils/kibanaUtils.plugin.js', + '{{regularBundlePath}}/plugin/esUiShared/esUiShared.plugin.js', + '{{regularBundlePath}}/plugin/kibanaReact/kibanaReact.plugin.js' + ], function () { + load([ + '{{regularBundlePath}}/{{appId}}.bundle.js', + {{#each styleSheetPaths}} + '{{this}}', + {{/each}} + ]) }); }; } diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx index c98fa612dc7af..322d734d9f39f 100644 --- a/src/plugins/dashboard/public/plugin.tsx +++ b/src/plugins/dashboard/public/plugin.tsx @@ -284,7 +284,7 @@ export class DashboardPlugin const { notifications } = core; const { uiActions, - data: { indexPatterns }, + data: { indexPatterns, search }, } = plugins; const SavedObjectFinder = getSavedObjectFinder(core.savedObjects, core.uiSettings); @@ -300,6 +300,7 @@ export class DashboardPlugin const savedDashboardLoader = createSavedDashboardLoader({ savedObjectsClient: core.savedObjects.client, indexPatterns, + search, chrome: core.chrome, overlays: core.overlays, }); diff --git a/src/plugins/dashboard/public/saved_dashboards/saved_dashboards.ts b/src/plugins/dashboard/public/saved_dashboards/saved_dashboards.ts index 2a1e64fa88a02..09357072a13a6 100644 --- a/src/plugins/dashboard/public/saved_dashboards/saved_dashboards.ts +++ b/src/plugins/dashboard/public/saved_dashboards/saved_dashboards.ts @@ -18,13 +18,14 @@ */ import { SavedObjectsClientContract, ChromeStart, OverlayStart } from 'kibana/public'; -import { IndexPatternsContract } from '../../../../plugins/data/public'; +import { DataPublicPluginStart, IndexPatternsContract } from '../../../../plugins/data/public'; import { SavedObjectLoader } from '../../../../plugins/saved_objects/public'; import { createSavedDashboardClass } from './saved_dashboard'; interface Services { savedObjectsClient: SavedObjectsClientContract; indexPatterns: IndexPatternsContract; + search: DataPublicPluginStart['search']; chrome: ChromeStart; overlays: OverlayStart; } diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index efafea44167d4..06a46065baa84 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -366,6 +366,7 @@ export { SearchStrategyProvider, ISearchSource, SearchSource, + createSearchSource, SearchSourceFields, EsQuerySortValue, SortDirection, diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts index eac122ba40c87..1723545b32522 100644 --- a/src/plugins/data/public/plugin.ts +++ b/src/plugins/data/public/plugin.ts @@ -155,7 +155,7 @@ export class DataPublicPlugin implements Plugin({ timefilter: { timefil // @public (undocumented) export const createSavedQueryService: (savedObjectsClient: Pick) => SavedQueryService; +// @public +export const createSearchSource: (indexPatterns: Pick) => (searchSourceJson: string, references: SavedObjectReference[]) => Promise; + // Warning: (ae-missing-release-tag) "CustomFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1667,6 +1671,10 @@ export class SearchSource { // (undocumented) history: SearchRequest[]; onRequestStart(handler: (searchSource: ISearchSource, options?: FetchOptions) => Promise): void; + serialize(): { + searchSourceJSON: string; + references: SavedObjectReference[]; + }; // (undocumented) setField(field: K, value: SearchSourceFields[K]): this; // (undocumented) @@ -1881,21 +1889,21 @@ export type TSearchStrategyProvider = (context: ISearc // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "getRoutes" needs to be exported by the entry point index.d.ts // src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:382:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:382:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:382:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:382:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:387:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:388:1 - (ae-forgotten-export) The symbol "convertDateRangeToString" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:390:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:399:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:401:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:404:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:405:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:408:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:409:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts -// src/plugins/data/public/index.ts:412:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:383:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:388:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:389:1 - (ae-forgotten-export) The symbol "convertDateRangeToString" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:391:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:400:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:401:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:402:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:405:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:406:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:409:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:410:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts +// src/plugins/data/public/index.ts:413:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:33:33 - (ae-forgotten-export) The symbol "FilterStateStore" needs to be exported by the entry point index.d.ts // src/plugins/data/public/query/state_sync/connect_to_query_state.ts:37:1 - (ae-forgotten-export) The symbol "QueryStateChange" needs to be exported by the entry point index.d.ts // src/plugins/data/public/types.ts:52:5 - (ae-forgotten-export) The symbol "createFiltersFromEvent" needs to be exported by the entry point index.d.ts diff --git a/src/plugins/data/public/search/aggs/buckets/date_histogram.ts b/src/plugins/data/public/search/aggs/buckets/date_histogram.ts index e6fd259fabc92..57f3aa85ad944 100644 --- a/src/plugins/data/public/search/aggs/buckets/date_histogram.ts +++ b/src/plugins/data/public/search/aggs/buckets/date_histogram.ts @@ -44,20 +44,15 @@ const updateTimeBuckets = ( timefilter: TimefilterContract, customBuckets?: IBucketDateHistogramAggConfig['buckets'] ) => { - const bounds = agg.params.timeRange ? timefilter.calculateBounds(agg.params.timeRange) : null; + const bounds = + agg.params.timeRange && agg.fieldIsTimeField() + ? timefilter.calculateBounds(agg.params.timeRange) + : undefined; const buckets = customBuckets || agg.buckets; - buckets.setBounds(agg.fieldIsTimeField() && bounds); + buckets.setBounds(bounds); buckets.setInterval(agg.params.interval); }; -// TODO: Need to incorporate these properly into TimeBuckets -interface ITimeBuckets { - setBounds: Function; - getScaledDateFormat: TimeBuckets['getScaledDateFormat']; - setInterval: Function; - getInterval: Function; -} - export interface DateHistogramBucketAggDependencies { uiSettings: IUiSettingsClient; query: QuerySetup; @@ -65,7 +60,7 @@ export interface DateHistogramBucketAggDependencies { } export interface IBucketDateHistogramAggConfig extends IBucketAggConfig { - buckets: ITimeBuckets; + buckets: TimeBuckets; } export function isDateHistogramBucketAggConfig(agg: any): agg is IBucketDateHistogramAggConfig { @@ -113,7 +108,12 @@ export const getDateHistogramBucketAgg = ({ if (buckets) return buckets; const { timefilter } = query.timefilter; - buckets = new TimeBuckets({ uiSettings }); + buckets = new TimeBuckets({ + 'histogram:maxBars': uiSettings.get('histogram:maxBars'), + 'histogram:barTarget': uiSettings.get('histogram:barTarget'), + dateFormat: uiSettings.get('dateFormat'), + 'dateFormat:scaled': uiSettings.get('dateFormat:scaled'), + }); updateTimeBuckets(this, timefilter, buckets); return buckets; @@ -206,7 +206,8 @@ export const getDateHistogramBucketAgg = ({ ...dateHistogramInterval(interval.expression), }; - const scaleMetrics = scaleMetricValues && interval.scaled && interval.scale < 1; + const scaleMetrics = + scaleMetricValues && interval.scaled && interval.scale && interval.scale < 1; if (scaleMetrics && aggs) { const metrics = aggs.aggs.filter(a => isMetricAggType(a.type)); const all = every(metrics, (a: IBucketAggConfig) => { @@ -218,7 +219,7 @@ export const getDateHistogramBucketAgg = ({ }); if (all) { output.metricScale = interval.scale; - output.metricScaleText = interval.preScaled.description; + output.metricScaleText = interval.preScaled?.description || ''; } } }, diff --git a/src/plugins/data/public/search/aggs/buckets/lib/time_buckets/time_buckets.test.ts b/src/plugins/data/public/search/aggs/buckets/lib/time_buckets/time_buckets.test.ts new file mode 100644 index 0000000000000..af3c15167295c --- /dev/null +++ b/src/plugins/data/public/search/aggs/buckets/lib/time_buckets/time_buckets.test.ts @@ -0,0 +1,121 @@ +/* + * 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. + */ +import moment from 'moment'; + +import { TimeBuckets, TimeBucketsConfig } from './time_buckets'; + +describe('TimeBuckets', () => { + const timeBucketConfig: TimeBucketsConfig = { + 'histogram:maxBars': 4, + 'histogram:barTarget': 3, + dateFormat: 'YYYY-MM-DD', + 'dateFormat:scaled': [ + ['', 'HH:mm:ss.SSS'], + ['PT1S', 'HH:mm:ss'], + ['PT1M', 'HH:mm'], + ['PT1H', 'YYYY-MM-DD HH:mm'], + ['P1DT', 'YYYY-MM-DD'], + ['P1YT', 'YYYY'], + ], + }; + + test('setBounds/getBounds - bounds is correct', () => { + const timeBuckets = new TimeBuckets(timeBucketConfig); + const bounds = { + min: moment('2020-03-25'), + max: moment('2020-03-31'), + }; + timeBuckets.setBounds(bounds); + const timeBucketsBounds = timeBuckets.getBounds(); + + expect(timeBucketsBounds).toEqual(bounds); + }); + + test('setBounds/getBounds - bounds is undefined', () => { + const timeBuckets = new TimeBuckets(timeBucketConfig); + const bounds = { + min: moment('2020-03-25'), + max: moment('2020-03-31'), + }; + timeBuckets.setBounds(bounds); + let timeBucketsBounds = timeBuckets.getBounds(); + + expect(timeBucketsBounds).toEqual(bounds); + + timeBuckets.setBounds(); + timeBucketsBounds = timeBuckets.getBounds(); + + expect(timeBucketsBounds).toBeUndefined(); + }); + + test('setInterval/getInterval - intreval is a string', () => { + const timeBuckets = new TimeBuckets(timeBucketConfig); + timeBuckets.setInterval('20m'); + const interval = timeBuckets.getInterval(); + + expect(interval.description).toEqual('20 minutes'); + expect(interval.esValue).toEqual(20); + expect(interval.esUnit).toEqual('m'); + expect(interval.expression).toEqual('20m'); + }); + + test('setInterval/getInterval - intreval is a string and bounds is defined', () => { + const timeBuckets = new TimeBuckets(timeBucketConfig); + const bounds = { + min: moment('2020-03-25'), + max: moment('2020-03-31'), + }; + timeBuckets.setBounds(bounds); + timeBuckets.setInterval('20m'); + const interval = timeBuckets.getInterval(); + + expect(interval.description).toEqual('day'); + expect(interval.esValue).toEqual(1); + expect(interval.esUnit).toEqual('d'); + expect(interval.expression).toEqual('1d'); + expect(interval.scaled).toBeTruthy(); + expect(interval.scale).toEqual(0.013888888888888888); + + if (interval.preScaled) { + expect(interval.preScaled.description).toEqual('20 minutes'); + expect(interval.preScaled.esValue).toEqual(20); + expect(interval.preScaled.esUnit).toEqual('m'); + expect(interval.preScaled.expression).toEqual('20m'); + } + }); + + test('setInterval/getInterval - intreval is a "auto"', () => { + const timeBuckets = new TimeBuckets(timeBucketConfig); + timeBuckets.setInterval('auto'); + const interval = timeBuckets.getInterval(); + + expect(interval.description).toEqual('0 milliseconds'); + expect(interval.esValue).toEqual(0); + expect(interval.esUnit).toEqual('ms'); + expect(interval.expression).toEqual('0ms'); + }); + + test('getScaledDateFormat', () => { + const timeBuckets = new TimeBuckets(timeBucketConfig); + timeBuckets.setInterval('20m'); + timeBuckets.getScaledDateFormat(); + const format = timeBuckets.getScaledDateFormat(); + expect(format).toEqual('HH:mm'); + }); +}); diff --git a/src/plugins/data/public/search/aggs/buckets/lib/time_buckets/time_buckets.ts b/src/plugins/data/public/search/aggs/buckets/lib/time_buckets/time_buckets.ts index c14f02e7decdf..b8d6586652d6b 100644 --- a/src/plugins/data/public/search/aggs/buckets/lib/time_buckets/time_buckets.ts +++ b/src/plugins/data/public/search/aggs/buckets/lib/time_buckets/time_buckets.ts @@ -17,11 +17,11 @@ * under the License. */ -import _ from 'lodash'; -import moment from 'moment'; +import { isString, isObject as isObjectLodash, isPlainObject, sortBy } from 'lodash'; +import moment, { Moment } from 'moment'; -import { IUiSettingsClient } from 'src/core/public'; import { parseInterval } from '../../../../../../common'; +import { TimeRangeBounds } from '../../../../../query'; import { calcAutoIntervalLessThan, calcAutoIntervalNear } from './calc_auto_interval'; import { convertDurationToNormalizedEsInterval, @@ -29,37 +29,30 @@ import { EsInterval, } from './calc_es_interval'; -interface Bounds { - min: Date | number | null; - max: Date | number | null; -} - interface TimeBucketsInterval extends moment.Duration { // TODO double-check whether all of these are needed description: string; esValue: EsInterval['value']; esUnit: EsInterval['unit']; expression: EsInterval['expression']; - overflow: moment.Duration | boolean; - preScaled?: moment.Duration; + preScaled?: TimeBucketsInterval; scale?: number; scaled?: boolean; } function isObject(o: any): o is Record { - return _.isObject(o); -} - -function isString(s: any): s is string { - return _.isString(s); + return isObjectLodash(o); } function isValidMoment(m: any): boolean { return m && 'isValid' in m && m.isValid(); } -interface TimeBucketsConfig { - uiSettings: IUiSettingsClient; +export interface TimeBucketsConfig { + 'histogram:maxBars': number; + 'histogram:barTarget': number; + dateFormat: string; + 'dateFormat:scaled': string[][]; } /** @@ -70,108 +63,17 @@ interface TimeBucketsConfig { * @param {[type]} display [description] */ export class TimeBuckets { - private getConfig: (key: string) => any; - - private _lb: Bounds['min'] = null; - private _ub: Bounds['max'] = null; + private _timeBucketConfig: TimeBucketsConfig; + private _lb: TimeRangeBounds['min']; + private _ub: TimeRangeBounds['max']; private _originalInterval: string | null = null; private _i?: moment.Duration | 'auto'; // because other parts of Kibana arbitrarily add properties [key: string]: any; - static __cached__(self: TimeBuckets) { - let cache: any = {}; - const sameMoment = same(moment.isMoment); - const sameDuration = same(moment.isDuration); - - const desc: Record = { - __cached__: { - value: self, - }, - }; - - const breakers: Record = { - setBounds: 'bounds', - clearBounds: 'bounds', - setInterval: 'interval', - }; - - const resources: Record = { - bounds: { - setup() { - return [self._lb, self._ub]; - }, - changes(prev: any) { - return !sameMoment(prev[0], self._lb) || !sameMoment(prev[1], self._ub); - }, - }, - interval: { - setup() { - return self._i; - }, - changes(prev: any) { - return !sameDuration(prev, self._i); - }, - }, - }; - - function cachedGetter(prop: string) { - return { - value: (...rest: any) => { - if (cache.hasOwnProperty(prop)) { - return cache[prop]; - } - - return (cache[prop] = self[prop](...rest)); - }, - }; - } - - function cacheBreaker(prop: string) { - const resource = resources[breakers[prop]]; - const setup = resource.setup; - const changes = resource.changes; - const fn = self[prop]; - - return { - value: (...args: any) => { - const prev = setup.call(self); - const ret = fn.apply(self, ...args); - - if (changes.call(self, prev)) { - cache = {}; - } - - return ret; - }, - }; - } - - function same(checkType: any) { - return function(a: any, b: any) { - if (a === b) return true; - if (checkType(a) === checkType(b)) return +a === +b; - return false; - }; - } - - _.forOwn(TimeBuckets.prototype, (fn, prop) => { - if (!prop || prop[0] === '_') return; - - if (breakers.hasOwnProperty(prop)) { - desc[prop] = cacheBreaker(prop); - } else { - desc[prop] = cachedGetter(prop); - } - }); - - return Object.create(self, desc); - } - - constructor({ uiSettings }: TimeBucketsConfig) { - this.getConfig = (key: string) => uiSettings.get(key); - return TimeBuckets.__cached__(this); + constructor(timeBucketConfig: TimeBucketsConfig) { + this._timeBucketConfig = timeBucketConfig; } /** @@ -182,10 +84,10 @@ export class TimeBuckets { * @return {moment.duration|undefined} */ private getDuration(): moment.Duration | undefined { - if (this._ub === null || this._lb === null || !this.hasBounds()) { + if (this._ub === undefined || this._lb === undefined || !this.hasBounds()) { return; } - const difference = (this._ub as number) - (this._lb as number); + const difference = this._ub.valueOf() - this._lb.valueOf(); return moment.duration(difference, 'ms'); } @@ -200,22 +102,20 @@ export class TimeBuckets { * * @returns {undefined} */ - setBounds(input?: Bounds | Bounds[]) { + setBounds(input?: TimeRangeBounds | TimeRangeBounds[]) { if (!input) return this.clearBounds(); let bounds; - if (_.isPlainObject(input) && !Array.isArray(input)) { + if (isPlainObject(input) && !Array.isArray(input)) { // accept the response from timefilter.getActiveBounds() bounds = [input.min, input.max]; } else { bounds = Array.isArray(input) ? input : []; } - const moments = _(bounds) - .map(_.ary(moment, 1)) - .sortBy(Number); + const moments: Moment[] = sortBy(bounds, Number); - const valid = moments.size() === 2 && moments.every(isValidMoment); + const valid = moments.length === 2 && moments.every(isValidMoment); if (!valid) { this.clearBounds(); throw new Error('invalid bounds set: ' + input); @@ -236,7 +136,7 @@ export class TimeBuckets { * @return {undefined} */ clearBounds() { - this._lb = this._ub = null; + this._lb = this._ub = undefined; } /** @@ -262,7 +162,7 @@ export class TimeBuckets { * object * */ - getBounds(): Bounds | undefined { + getBounds(): TimeRangeBounds | undefined { if (!this.hasBounds()) return; return { min: this._lb, @@ -278,11 +178,10 @@ export class TimeBuckets { * - Any object from src/legacy/ui/agg_types.js * - "auto" * - Pass a valid moment unit - * - a moment.duration object. * * @param {object|string|moment.duration} input - see desc */ - setInterval(input: null | string | Record | moment.Duration) { + setInterval(input: null | string | Record) { let interval = input; // selection object -> val @@ -351,7 +250,7 @@ export class TimeBuckets { const readInterval = () => { const interval = this._i; if (moment.isDuration(interval)) return interval; - return calcAutoIntervalNear(this.getConfig('histogram:barTarget'), Number(duration)); + return calcAutoIntervalNear(this._timeBucketConfig['histogram:barTarget'], Number(duration)); }; const parsedInterval = readInterval(); @@ -362,7 +261,7 @@ export class TimeBuckets { return interval; } - const maxLength: number = this.getConfig('histogram:maxBars'); + const maxLength: number = this._timeBucketConfig['histogram:maxBars']; const approxLen = Number(duration) / Number(interval); let scaled; @@ -396,10 +295,6 @@ export class TimeBuckets { esValue: esInterval.value, esUnit: esInterval.unit, expression: esInterval.expression, - overflow: - Number(duration) > Number(interval) - ? moment.duration(Number(interval) - Number(duration)) - : false, }); }; @@ -423,7 +318,7 @@ export class TimeBuckets { */ getScaledDateFormat() { const interval = this.getInterval(); - const rules = this.getConfig('dateFormat:scaled'); + const rules = this._timeBucketConfig['dateFormat:scaled']; for (let i = rules.length - 1; i >= 0; i--) { const rule = rules[i]; @@ -432,6 +327,6 @@ export class TimeBuckets { } } - return this.getConfig('dateFormat'); + return this._timeBucketConfig.dateFormat; } } diff --git a/src/plugins/data/public/search/aggs/utils/calculate_auto_time_expression.ts b/src/plugins/data/public/search/aggs/utils/calculate_auto_time_expression.ts index 459de66d057d4..9d976784329cc 100644 --- a/src/plugins/data/public/search/aggs/utils/calculate_auto_time_expression.ts +++ b/src/plugins/data/public/search/aggs/utils/calculate_auto_time_expression.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import moment from 'moment'; import { IUiSettingsClient } from 'src/core/public'; import { TimeBuckets } from '../buckets/lib/time_buckets'; import { toAbsoluteDates, TimeRange } from '../../../../common'; @@ -28,12 +28,17 @@ export function getCalculateAutoTimeExpression(uiSettings: IUiSettingsClient) { return; } - const buckets = new TimeBuckets({ uiSettings }); + const buckets = new TimeBuckets({ + 'histogram:maxBars': uiSettings.get('histogram:maxBars'), + 'histogram:barTarget': uiSettings.get('histogram:barTarget'), + dateFormat: uiSettings.get('dateFormat'), + 'dateFormat:scaled': uiSettings.get('dateFormat:scaled'), + }); buckets.setInterval('auto'); buckets.setBounds({ - min: dates.from, - max: dates.to, + min: moment(dates.from), + max: moment(dates.to), }); return buckets.getInterval().expression; diff --git a/src/plugins/data/public/search/index.ts b/src/plugins/data/public/search/index.ts index 1687d749f46e2..cce973d632f41 100644 --- a/src/plugins/data/public/search/index.ts +++ b/src/plugins/data/public/search/index.ts @@ -54,6 +54,7 @@ export { SearchSourceFields, EsQuerySortValue, SortDirection, + createSearchSource, } from './search_source'; export { SearchInterceptor } from './search_interceptor'; diff --git a/src/plugins/data/public/search/mocks.ts b/src/plugins/data/public/search/mocks.ts index b70e889066a45..cb1c625a72959 100644 --- a/src/plugins/data/public/search/mocks.ts +++ b/src/plugins/data/public/search/mocks.ts @@ -33,6 +33,7 @@ export const searchStartMock: jest.Mocked = { aggs: searchAggsStartMock(), setInterceptor: jest.fn(), search: jest.fn(), + createSearchSource: jest.fn(), __LEGACY: { AggConfig: jest.fn() as any, AggType: jest.fn(), diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index af57f1505029c..716f557f84de3 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -25,6 +25,8 @@ import { TStrategyTypes } from './strategy_types'; import { getEsClient, LegacyApiCaller } from './es_client'; import { ES_SEARCH_STRATEGY, DEFAULT_SEARCH_STRATEGY } from '../../common/search'; import { esSearchStrategyProvider } from './es_search/es_search_strategy'; +import { IndexPatternsContract } from '../index_patterns/index_patterns'; +import { createSearchSource } from './search_source'; import { QuerySetup } from '../query/query_service'; import { GetInternalStartServicesFn } from '../types'; import { SearchInterceptor } from './search_interceptor'; @@ -52,6 +54,7 @@ interface SearchServiceSetupDependencies { interface SearchStartDependencies { fieldFormats: FieldFormatsStart; + indexPatterns: IndexPatternsContract; } /** @@ -114,7 +117,10 @@ export class SearchService implements Plugin { }; } - public start(core: CoreStart, { fieldFormats }: SearchStartDependencies): ISearchStart { + public start( + core: CoreStart, + { fieldFormats, indexPatterns }: SearchStartDependencies + ): ISearchStart { /** * A global object that intercepts all searches and provides convenience methods for cancelling * all pending search requests, as well as getting the number of pending search requests. @@ -156,6 +162,7 @@ export class SearchService implements Plugin { // TODO: should an intercepror have a destroy method? this.searchInterceptor = searchInterceptor; }, + createSearchSource: createSearchSource(indexPatterns), __LEGACY: { esClient: this.esClient!, AggConfig, diff --git a/src/plugins/data/public/search/search_source/create_search_source.test.ts b/src/plugins/data/public/search/search_source/create_search_source.test.ts new file mode 100644 index 0000000000000..d49ce5a0d11f8 --- /dev/null +++ b/src/plugins/data/public/search/search_source/create_search_source.test.ts @@ -0,0 +1,151 @@ +/* + * 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. + */ +import { createSearchSource as createSearchSourceFactory } from './create_search_source'; +import { IIndexPattern } from '../../../common/index_patterns'; +import { IndexPatternsContract } from '../../index_patterns/index_patterns'; +import { Filter } from '../../../common/es_query/filters'; + +describe('createSearchSource', function() { + let createSearchSource: ReturnType; + const indexPatternMock: IIndexPattern = {} as IIndexPattern; + let indexPatternContractMock: jest.Mocked; + + beforeEach(() => { + indexPatternContractMock = ({ + get: jest.fn().mockReturnValue(Promise.resolve(indexPatternMock)), + } as unknown) as jest.Mocked; + createSearchSource = createSearchSourceFactory(indexPatternContractMock); + }); + + it('should fail if JSON is invalid', () => { + expect(createSearchSource('{', [])).rejects.toThrow(); + expect(createSearchSource('0', [])).rejects.toThrow(); + expect(createSearchSource('"abcdefg"', [])).rejects.toThrow(); + }); + + it('should set fields', async () => { + const searchSource = await createSearchSource( + JSON.stringify({ + highlightAll: true, + query: { + query: '', + language: 'kuery', + }, + }), + [] + ); + expect(searchSource.getOwnField('highlightAll')).toBe(true); + expect(searchSource.getOwnField('query')).toEqual({ + query: '', + language: 'kuery', + }); + }); + + it('should resolve referenced index pattern', async () => { + const searchSource = await createSearchSource( + JSON.stringify({ + indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.index', + }), + [ + { + id: '123-456', + type: 'index-pattern', + name: 'kibanaSavedObjectMeta.searchSourceJSON.index', + }, + ] + ); + expect(indexPatternContractMock.get).toHaveBeenCalledWith('123-456'); + expect(searchSource.getOwnField('index')).toBe(indexPatternMock); + }); + + it('should set filters and resolve referenced index patterns', async () => { + const searchSource = await createSearchSource( + JSON.stringify({ + filter: [ + { + meta: { + alias: null, + negate: false, + disabled: false, + type: 'phrase', + key: 'category.keyword', + params: { + query: "Men's Clothing", + }, + indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index', + }, + query: { + match_phrase: { + 'category.keyword': "Men's Clothing", + }, + }, + $state: { + store: 'appState', + }, + }, + ], + }), + [ + { + id: '123-456', + type: 'index-pattern', + name: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index', + }, + ] + ); + const filters = searchSource.getOwnField('filter') as Filter[]; + expect(filters[0]).toMatchInlineSnapshot(` + Object { + "$state": Object { + "store": "appState", + }, + "meta": Object { + "alias": null, + "disabled": false, + "index": "123-456", + "key": "category.keyword", + "negate": false, + "params": Object { + "query": "Men's Clothing", + }, + "type": "phrase", + }, + "query": Object { + "match_phrase": Object { + "category.keyword": "Men's Clothing", + }, + }, + } + `); + }); + + it('should migrate legacy queries on the fly', async () => { + const searchSource = await createSearchSource( + JSON.stringify({ + highlightAll: true, + query: 'a:b', + }), + [] + ); + expect(searchSource.getOwnField('query')).toEqual({ + query: 'a:b', + language: 'lucene', + }); + }); +}); diff --git a/src/plugins/data/public/search/search_source/create_search_source.ts b/src/plugins/data/public/search/search_source/create_search_source.ts new file mode 100644 index 0000000000000..35b7ac4eb9762 --- /dev/null +++ b/src/plugins/data/public/search/search_source/create_search_source.ts @@ -0,0 +1,113 @@ +/* + * 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. + */ +import _ from 'lodash'; +import { SavedObjectReference } from 'kibana/public'; +import { migrateLegacyQuery } from '../../../../kibana_legacy/public'; +import { InvalidJSONProperty } from '../../../../kibana_utils/public'; +import { SearchSource } from './search_source'; +import { IndexPatternsContract } from '../../index_patterns/index_patterns'; +import { SearchSourceFields } from './types'; + +/** + * Deserializes a json string and a set of referenced objects to a `SearchSource` instance. + * Use this method to re-create the search source serialized using `searchSource.serialize`. + * + * This function is a factory function that returns the actual utility when calling it with the + * required service dependency (index patterns contract). A pre-wired version is also exposed in + * the start contract of the data plugin as part of the search service + * + * @param indexPatterns The index patterns contract of the data plugin + * + * @return Wired utility function taking two parameters `searchSourceJson`, the json string + * returned by `serializeSearchSource` and `references`, a list of references including the ones + * returned by `serializeSearchSource`. + * + * @public */ +export const createSearchSource = (indexPatterns: IndexPatternsContract) => async ( + searchSourceJson: string, + references: SavedObjectReference[] +) => { + const searchSource = new SearchSource(); + + // if we have a searchSource, set its values based on the searchSourceJson field + let searchSourceValues: Record; + try { + searchSourceValues = JSON.parse(searchSourceJson); + } catch (e) { + throw new InvalidJSONProperty( + `Invalid JSON in search source. ${e.message} JSON: ${searchSourceJson}` + ); + } + + // This detects a scenario where documents with invalid JSON properties have been imported into the saved object index. + // (This happened in issue #20308) + if (!searchSourceValues || typeof searchSourceValues !== 'object') { + throw new InvalidJSONProperty('Invalid JSON in search source.'); + } + + // Inject index id if a reference is saved + if (searchSourceValues.indexRefName) { + const reference = references.find(ref => ref.name === searchSourceValues.indexRefName); + if (!reference) { + throw new Error(`Could not find reference for ${searchSourceValues.indexRefName}`); + } + searchSourceValues.index = reference.id; + delete searchSourceValues.indexRefName; + } + + if (searchSourceValues.filter && Array.isArray(searchSourceValues.filter)) { + searchSourceValues.filter.forEach((filterRow: any) => { + if (!filterRow.meta || !filterRow.meta.indexRefName) { + return; + } + const reference = references.find((ref: any) => ref.name === filterRow.meta.indexRefName); + if (!reference) { + throw new Error(`Could not find reference for ${filterRow.meta.indexRefName}`); + } + filterRow.meta.index = reference.id; + delete filterRow.meta.indexRefName; + }); + } + + if (searchSourceValues.index && typeof searchSourceValues.index === 'string') { + searchSourceValues.index = await indexPatterns.get(searchSourceValues.index); + } + + const searchSourceFields = searchSource.getFields(); + const fnProps = _.transform( + searchSourceFields, + function(dynamic, val, name) { + if (_.isFunction(val) && name) dynamic[name] = val; + }, + {} + ); + + // This assignment might hide problems because the type of values passed from the parsed JSON + // might not fit the SearchSourceFields interface. + const newFields: SearchSourceFields = _.defaults(searchSourceValues, fnProps); + + searchSource.setFields(newFields); + const query = searchSource.getOwnField('query'); + + if (typeof query !== 'undefined') { + searchSource.setField('query', migrateLegacyQuery(query)); + } + + return searchSource; +}; diff --git a/src/plugins/data/public/search/search_source/index.ts b/src/plugins/data/public/search/search_source/index.ts index 10f1b2bc332e1..0e9f530d0968a 100644 --- a/src/plugins/data/public/search/search_source/index.ts +++ b/src/plugins/data/public/search/search_source/index.ts @@ -18,4 +18,5 @@ */ export * from './search_source'; +export { createSearchSource } from './create_search_source'; export { SortDirection, EsQuerySortValue, SearchSourceFields } from './types'; diff --git a/src/plugins/data/public/search/search_source/mocks.ts b/src/plugins/data/public/search/search_source/mocks.ts index 700bea741bd6a..1ef7c1187a9e0 100644 --- a/src/plugins/data/public/search/search_source/mocks.ts +++ b/src/plugins/data/public/search/search_source/mocks.ts @@ -37,4 +37,5 @@ export const searchSourceMock: MockedKeys = { getSearchRequestBody: jest.fn(), destroy: jest.fn(), history: [], + serialize: jest.fn(), }; diff --git a/src/plugins/data/public/search/search_source/search_source.test.ts b/src/plugins/data/public/search/search_source/search_source.test.ts index fcd116a3f4121..6bad093d31402 100644 --- a/src/plugins/data/public/search/search_source/search_source.test.ts +++ b/src/plugins/data/public/search/search_source/search_source.test.ts @@ -18,7 +18,7 @@ */ import { SearchSource } from './search_source'; -import { IndexPattern } from '../..'; +import { IndexPattern, SortDirection } from '../..'; import { mockDataServices } from '../aggs/test_helpers'; jest.mock('../fetch', () => ({ @@ -150,4 +150,77 @@ describe('SearchSource', function() { expect(parentFn).toBeCalledWith(searchSource, options); }); }); + + describe('#serialize', function() { + it('should reference index patterns', () => { + const indexPattern123 = { id: '123' } as IndexPattern; + const searchSource = new SearchSource(); + searchSource.setField('index', indexPattern123); + const { searchSourceJSON, references } = searchSource.serialize(); + expect(references[0].id).toEqual('123'); + expect(references[0].type).toEqual('index-pattern'); + expect(JSON.parse(searchSourceJSON).indexRefName).toEqual(references[0].name); + }); + + it('should add other fields', () => { + const searchSource = new SearchSource(); + searchSource.setField('highlightAll', true); + searchSource.setField('from', 123456); + const { searchSourceJSON } = searchSource.serialize(); + expect(JSON.parse(searchSourceJSON).highlightAll).toEqual(true); + expect(JSON.parse(searchSourceJSON).from).toEqual(123456); + }); + + it('should omit sort and size', () => { + const searchSource = new SearchSource(); + searchSource.setField('highlightAll', true); + searchSource.setField('from', 123456); + searchSource.setField('sort', { field: SortDirection.asc }); + searchSource.setField('size', 200); + const { searchSourceJSON } = searchSource.serialize(); + expect(Object.keys(JSON.parse(searchSourceJSON))).toEqual(['highlightAll', 'from']); + }); + + it('should serialize filters', () => { + const searchSource = new SearchSource(); + const filter = [ + { + query: 'query', + meta: { + alias: 'alias', + disabled: false, + negate: false, + }, + }, + ]; + searchSource.setField('filter', filter); + const { searchSourceJSON } = searchSource.serialize(); + expect(JSON.parse(searchSourceJSON).filter).toEqual(filter); + }); + + it('should reference index patterns in filters separately from index field', () => { + const searchSource = new SearchSource(); + const indexPattern123 = { id: '123' } as IndexPattern; + searchSource.setField('index', indexPattern123); + const filter = [ + { + query: 'query', + meta: { + alias: 'alias', + disabled: false, + negate: false, + index: '456', + }, + }, + ]; + searchSource.setField('filter', filter); + const { searchSourceJSON, references } = searchSource.serialize(); + expect(references[0].id).toEqual('123'); + expect(references[0].type).toEqual('index-pattern'); + expect(JSON.parse(searchSourceJSON).indexRefName).toEqual(references[0].name); + expect(references[1].id).toEqual('456'); + expect(references[1].type).toEqual('index-pattern'); + expect(JSON.parse(searchSourceJSON).filter[0].meta.indexRefName).toEqual(references[1].name); + }); + }); }); diff --git a/src/plugins/data/public/search/search_source/search_source.ts b/src/plugins/data/public/search/search_source/search_source.ts index 0c3321f03dabc..c70db7bb82ef7 100644 --- a/src/plugins/data/public/search/search_source/search_source.ts +++ b/src/plugins/data/public/search/search_source/search_source.ts @@ -70,6 +70,7 @@ */ import _ from 'lodash'; +import { SavedObjectReference } from 'kibana/public'; import { normalizeSortRequest } from './normalize_sort_request'; import { filterDocvalueFields } from './filter_docvalue_fields'; import { fieldWildcardFilter } from '../../../../kibana_utils/public'; @@ -419,4 +420,85 @@ export class SearchSource { return searchRequest; } + + /** + * Serializes the instance to a JSON string and a set of referenced objects. + * Use this method to get a representation of the search source which can be stored in a saved object. + * + * The references returned by this function can be mixed with other references in the same object, + * however make sure there are no name-collisions. The references will be named `kibanaSavedObjectMeta.searchSourceJSON.index` + * and `kibanaSavedObjectMeta.searchSourceJSON.filter[].meta.index`. + * + * Using `createSearchSource`, the instance can be re-created. + * @param searchSource The search source to serialize + * @public */ + public serialize() { + const references: SavedObjectReference[] = []; + + const { + filter: originalFilters, + ...searchSourceFields + }: Omit = _.omit(this.getFields(), ['sort', 'size']); + let serializedSearchSourceFields: Omit & { + indexRefName?: string; + filter?: Array & { meta: Filter['meta'] & { indexRefName?: string } }>; + } = searchSourceFields; + if (searchSourceFields.index) { + const indexId = searchSourceFields.index.id!; + const refName = 'kibanaSavedObjectMeta.searchSourceJSON.index'; + references.push({ + name: refName, + type: 'index-pattern', + id: indexId, + }); + serializedSearchSourceFields = { + ...serializedSearchSourceFields, + indexRefName: refName, + index: undefined, + }; + } + if (originalFilters) { + const filters = this.getFilters(originalFilters); + serializedSearchSourceFields = { + ...serializedSearchSourceFields, + filter: filters.map((filterRow, i) => { + if (!filterRow.meta || !filterRow.meta.index) { + return filterRow; + } + const refName = `kibanaSavedObjectMeta.searchSourceJSON.filter[${i}].meta.index`; + references.push({ + name: refName, + type: 'index-pattern', + id: filterRow.meta.index, + }); + return { + ...filterRow, + meta: { + ...filterRow.meta, + indexRefName: refName, + index: undefined, + }, + }; + }), + }; + } + + return { searchSourceJSON: JSON.stringify(serializedSearchSourceFields), references }; + } + + private getFilters(filterField: SearchSourceFields['filter']): Filter[] { + if (!filterField) { + return []; + } + + if (Array.isArray(filterField)) { + return filterField; + } + + if (_.isFunction(filterField)) { + return this.getFilters(filterField()); + } + + return [filterField]; + } } diff --git a/src/plugins/data/public/search/types.ts b/src/plugins/data/public/search/types.ts index 03cbfa9f8ed84..ba6e44f47b75e 100644 --- a/src/plugins/data/public/search/types.ts +++ b/src/plugins/data/public/search/types.ts @@ -18,6 +18,7 @@ */ import { CoreStart } from 'kibana/public'; +import { createSearchSource } from './search_source'; import { SearchAggsSetup, SearchAggsStart, SearchAggsStartLegacy } from './aggs'; import { ISearch, ISearchGeneric } from './i_search'; import { TStrategyTypes } from './strategy_types'; @@ -89,5 +90,6 @@ export interface ISearchStart { aggs: SearchAggsStart; setInterceptor: (searchInterceptor: SearchInterceptor) => void; search: ISearchGeneric; + createSearchSource: ReturnType; __LEGACY: ISearchStartLegacy & SearchAggsStartLegacy; } diff --git a/src/plugins/discover/public/services.ts b/src/plugins/discover/public/services.ts index 3a28759d82b71..37e2144800ea1 100644 --- a/src/plugins/discover/public/services.ts +++ b/src/plugins/discover/public/services.ts @@ -17,7 +17,7 @@ * under the License. */ -import { createGetterSetter } from '../../kibana_utils/common'; +import { createGetterSetter } from '../../kibana_utils/public'; import { DocViewsRegistry } from './doc_views/doc_views_registry'; export const [getDocViewsRegistry, setDocViewsRegistry] = createGetterSetter( diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable_action_storage.test.ts b/src/plugins/embeddable/public/lib/embeddables/embeddable_action_storage.test.ts index 56facc37fc666..ddd84b0544345 100644 --- a/src/plugins/embeddable/public/lib/embeddables/embeddable_action_storage.test.ts +++ b/src/plugins/embeddable/public/lib/embeddables/embeddable_action_storage.test.ts @@ -21,7 +21,7 @@ import { Embeddable } from './embeddable'; import { EmbeddableInput } from './i_embeddable'; import { ViewMode } from '../types'; import { EmbeddableActionStorage, SerializedEvent } from './embeddable_action_storage'; -import { of } from '../../../../kibana_utils/common'; +import { of } from '../../../../kibana_utils/public'; class TestEmbeddable extends Embeddable { public readonly type = 'test'; diff --git a/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx b/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx index 816001ba42ff1..715827a72c61b 100644 --- a/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx +++ b/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx @@ -20,8 +20,6 @@ import React from 'react'; import { ErrorEmbeddable } from './error_embeddable'; import { EmbeddableRoot } from './embeddable_root'; import { mount } from 'enzyme'; -// @ts-ignore -import { findTestSubject } from '@elastic/eui/lib/test'; test('ErrorEmbeddable renders an embeddable', async () => { const embeddable = new ErrorEmbeddable('some error occurred', { id: '123', title: 'Error' }); diff --git a/src/plugins/es_ui_shared/kibana.json b/src/plugins/es_ui_shared/kibana.json new file mode 100644 index 0000000000000..5a3db3b344090 --- /dev/null +++ b/src/plugins/es_ui_shared/kibana.json @@ -0,0 +1,5 @@ +{ + "id": "esUiShared", + "version": "kibana", + "ui": true +} diff --git a/src/plugins/es_ui_shared/public/index.ts b/src/plugins/es_ui_shared/public/index.ts index 944b800c66a28..6db6248f4c68f 100644 --- a/src/plugins/es_ui_shared/public/index.ts +++ b/src/plugins/es_ui_shared/public/index.ts @@ -35,3 +35,11 @@ export { export { indices } from './indices'; export { useUIAceKeyboardMode } from './use_ui_ace_keyboard_mode'; + +/** dummy plugin, we just want esUiShared to have its own bundle */ +export function plugin() { + return new (class EsUiSharedPlugin { + setup() {} + start() {} + })(); +} diff --git a/src/plugins/index_pattern_management/kibana.json b/src/plugins/index_pattern_management/kibana.json new file mode 100644 index 0000000000000..d5397a11184aa --- /dev/null +++ b/src/plugins/index_pattern_management/kibana.json @@ -0,0 +1,7 @@ +{ + "id": "indexPatternManagement", + "version": "kibana", + "server": false, + "ui": true, + "requiredPlugins": [] +} diff --git a/src/legacy/core_plugins/management/public/np_ready/index.ts b/src/plugins/index_pattern_management/public/index.ts similarity index 83% rename from src/legacy/core_plugins/management/public/np_ready/index.ts rename to src/plugins/index_pattern_management/public/index.ts index bae0f1d3e23cd..da482c0c51f0a 100644 --- a/src/legacy/core_plugins/management/public/np_ready/index.ts +++ b/src/plugins/index_pattern_management/public/index.ts @@ -29,14 +29,11 @@ * either types, or static code. */ import { PluginInitializerContext } from 'src/core/public'; -import { ManagementPlugin } from './plugin'; -export { ManagementSetup, ManagementStart } from './plugin'; +import { IndexPatternManagementPlugin } from './plugin'; +export { IndexPatternManagementSetup, IndexPatternManagementStart } from './plugin'; export function plugin(initializerContext: PluginInitializerContext) { - return new ManagementPlugin(initializerContext); + return new IndexPatternManagementPlugin(initializerContext); } -export { - IndexPatternCreationConfig, - IndexPatternListConfig, -} from './services/index_pattern_management'; +export { IndexPatternCreationConfig, IndexPatternListConfig } from './service'; diff --git a/src/legacy/core_plugins/management/public/np_ready/mocks.ts b/src/plugins/index_pattern_management/public/mocks.ts similarity index 57% rename from src/legacy/core_plugins/management/public/np_ready/mocks.ts rename to src/plugins/index_pattern_management/public/mocks.ts index ae0be98de63f3..bc97f46c302e3 100644 --- a/src/legacy/core_plugins/management/public/np_ready/mocks.ts +++ b/src/plugins/index_pattern_management/public/mocks.ts @@ -18,42 +18,38 @@ */ import { PluginInitializerContext } from 'src/core/public'; -import { coreMock } from '../../../../../core/public/mocks'; +import { coreMock } from '../../../core/public/mocks'; import { - ManagementSetup, - ManagementStart, - ManagementPlugin, - ManagementPluginSetupDependencies, + IndexPatternManagementSetup, + IndexPatternManagementStart, + IndexPatternManagementPlugin, } from './plugin'; -const createSetupContract = (): ManagementSetup => ({ - indexPattern: { - creation: { - add: jest.fn(), - getType: jest.fn(), - getIndexPatternCreationOptions: jest.fn(), - } as any, - list: { - add: jest.fn(), - getIndexPatternTags: jest.fn(), - getFieldInfo: jest.fn(), - areScriptedFieldsEnabled: jest.fn(), - } as any, - }, +const createSetupContract = (): IndexPatternManagementSetup => ({ + creation: { + addCreationConfig: jest.fn(), + } as any, + list: { + addListConfig: jest.fn(), + } as any, }); -const createStartContract = (): ManagementStart => ({}); +const createStartContract = (): IndexPatternManagementStart => ({ + creation: { + getType: jest.fn(), + getIndexPatternCreationOptions: jest.fn(), + } as any, + list: { + getIndexPatternTags: jest.fn(), + getFieldInfo: jest.fn(), + areScriptedFieldsEnabled: jest.fn(), + } as any, +}); const createInstance = async () => { - const plugin = new ManagementPlugin({} as PluginInitializerContext); + const plugin = new IndexPatternManagementPlugin({} as PluginInitializerContext); - const setup = plugin.setup(coreMock.createSetup(), ({ - home: { - featureCatalogue: { - register: jest.fn(), - }, - }, - } as unknown) as ManagementPluginSetupDependencies); + const setup = plugin.setup(coreMock.createSetup()); const doStart = () => plugin.start(coreMock.createStart(), {}); return { diff --git a/src/legacy/core_plugins/management/public/np_ready/plugin.ts b/src/plugins/index_pattern_management/public/plugin.ts similarity index 60% rename from src/legacy/core_plugins/management/public/np_ready/plugin.ts rename to src/plugins/index_pattern_management/public/plugin.ts index 2a8ef10c817cc..93bb0ead1df4a 100644 --- a/src/legacy/core_plugins/management/public/np_ready/plugin.ts +++ b/src/plugins/index_pattern_management/public/plugin.ts @@ -17,43 +17,40 @@ * under the License. */ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public'; -import { HomePublicPluginSetup } from 'src/plugins/home/public'; -import { IndexPatternManagementService, IndexPatternManagementSetup } from './services'; +import { + IndexPatternManagementService, + IndexPatternManagementServiceSetup, + IndexPatternManagementServiceStart, +} from './service'; -export interface ManagementPluginSetupDependencies { - home: HomePublicPluginSetup; -} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface IndexPatternManagementSetupDependencies {} // eslint-disable-next-line @typescript-eslint/no-empty-interface -interface ManagementPluginStartDependencies {} +export interface IndexPatternManagementStartDependencies {} -export interface ManagementSetup { - indexPattern: IndexPatternManagementSetup; -} +export type IndexPatternManagementSetup = IndexPatternManagementServiceSetup; -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface ManagementStart {} +export type IndexPatternManagementStart = IndexPatternManagementServiceStart; -export class ManagementPlugin +export class IndexPatternManagementPlugin implements Plugin< - ManagementSetup, - ManagementStart, - ManagementPluginSetupDependencies, - ManagementPluginStartDependencies + IndexPatternManagementSetup, + IndexPatternManagementStart, + IndexPatternManagementSetupDependencies, + IndexPatternManagementStartDependencies > { private readonly indexPattern = new IndexPatternManagementService(); constructor(initializerContext: PluginInitializerContext) {} - public setup(core: CoreSetup, { home }: ManagementPluginSetupDependencies) { - return { - indexPattern: this.indexPattern.setup({ httpClient: core.http, home }), - }; + public setup(core: CoreSetup) { + return this.indexPattern.setup({ httpClient: core.http }); } - public start(core: CoreStart, plugins: ManagementPluginStartDependencies) { - return {}; + public start(core: CoreStart, plugins: IndexPatternManagementStartDependencies) { + return this.indexPattern.start(); } public stop() { diff --git a/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/creation/config.ts b/src/plugins/index_pattern_management/public/service/creation/config.ts similarity index 88% rename from src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/creation/config.ts rename to src/plugins/index_pattern_management/public/service/creation/config.ts index 5714fa3338962..29ab0ebfc3d5f 100644 --- a/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/creation/config.ts +++ b/src/plugins/index_pattern_management/public/service/creation/config.ts @@ -18,20 +18,20 @@ */ import { i18n } from '@kbn/i18n'; -import { MatchedIndex } from '../../../../../../kibana/public/management/sections/index_patterns/create_index_pattern_wizard/types'; +import { MatchedIndex } from '../../../../../legacy/core_plugins/kibana/public/management/sections/index_patterns/create_index_pattern_wizard/types'; const indexPatternTypeName = i18n.translate( - 'management.editIndexPattern.createIndex.defaultTypeName', + 'indexPatternManagement.editIndexPattern.createIndex.defaultTypeName', { defaultMessage: 'index pattern' } ); const indexPatternButtonText = i18n.translate( - 'management.editIndexPattern.createIndex.defaultButtonText', + 'indexPatternManagement.editIndexPattern.createIndex.defaultButtonText', { defaultMessage: 'Standard index pattern' } ); const indexPatternButtonDescription = i18n.translate( - 'management.editIndexPattern.createIndex.defaultButtonDescription', + 'indexPatternManagement.editIndexPattern.createIndex.defaultButtonDescription', { defaultMessage: 'Perform full aggregations against any data' } ); diff --git a/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/creation/index.ts b/src/plugins/index_pattern_management/public/service/creation/index.ts similarity index 100% rename from src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/creation/index.ts rename to src/plugins/index_pattern_management/public/service/creation/index.ts diff --git a/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/creation/manager.ts b/src/plugins/index_pattern_management/public/service/creation/manager.ts similarity index 79% rename from src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/creation/manager.ts rename to src/plugins/index_pattern_management/public/service/creation/manager.ts index e7fa13409ab04..32b3e7ee7a133 100644 --- a/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/creation/manager.ts +++ b/src/plugins/index_pattern_management/public/service/creation/manager.ts @@ -17,23 +17,25 @@ * under the License. */ -import { HttpSetup } from '../../../../../../../../core/public'; +import { HttpSetup } from '../../../../../core/public'; import { IndexPatternCreationConfig, UrlHandler, IndexPatternCreationOption } from './config'; export class IndexPatternCreationManager { private configs: IndexPatternCreationConfig[]; - constructor(private readonly httpClient: HttpSetup) { + constructor() { this.configs = []; } - public add(Config: typeof IndexPatternCreationConfig) { - const config = new Config({ httpClient: this.httpClient }); + public addCreationConfig = (httpClient: HttpSetup) => ( + Config: typeof IndexPatternCreationConfig + ) => { + const config = new Config({ httpClient }); if (this.configs.findIndex(c => c.key === config.key) !== -1) { throw new Error(`${config.key} exists in IndexPatternCreationManager.`); } this.configs.push(config); - } + }; public getType(key: string | undefined): IndexPatternCreationConfig | null { if (key) { @@ -58,4 +60,13 @@ export class IndexPatternCreationManager { ); return options; } + + setup = (httpClient: HttpSetup) => ({ + addCreationConfig: this.addCreationConfig(httpClient).bind(this), + }); + + start = () => ({ + getType: this.getType.bind(this), + getIndexPatternCreationOptions: this.getIndexPatternCreationOptions.bind(this), + }); } diff --git a/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/index.ts b/src/plugins/index_pattern_management/public/service/index.ts similarity index 100% rename from src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/index.ts rename to src/plugins/index_pattern_management/public/service/index.ts diff --git a/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/index_pattern_management_service.ts b/src/plugins/index_pattern_management/public/service/index_pattern_management_service.ts similarity index 51% rename from src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/index_pattern_management_service.ts rename to src/plugins/index_pattern_management/public/service/index_pattern_management_service.ts index 2b6f008dd928a..4780fa00ed468 100644 --- a/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/index_pattern_management_service.ts +++ b/src/plugins/index_pattern_management/public/service/index_pattern_management_service.ts @@ -17,18 +17,12 @@ * under the License. */ -import { i18n } from '@kbn/i18n'; -import { - FeatureCatalogueCategory, - HomePublicPluginSetup, -} from '../../../../../../../plugins/home/public'; -import { HttpSetup } from '../../../../../../../core/public'; +import { HttpSetup } from '../../../../core/public'; import { IndexPatternCreationManager, IndexPatternCreationConfig } from './creation'; import { IndexPatternListManager, IndexPatternListConfig } from './list'; interface SetupDependencies { httpClient: HttpSetup; - home: HomePublicPluginSetup; } /** @@ -37,31 +31,29 @@ interface SetupDependencies { * @internal */ export class IndexPatternManagementService { - public setup({ httpClient, home }: SetupDependencies) { - const creation = new IndexPatternCreationManager(httpClient); - const list = new IndexPatternListManager(); + indexPatternCreationManager: IndexPatternCreationManager; + indexPatternListConfig: IndexPatternListManager; - creation.add(IndexPatternCreationConfig); - list.add(IndexPatternListConfig); + constructor() { + this.indexPatternCreationManager = new IndexPatternCreationManager(); + this.indexPatternListConfig = new IndexPatternListManager(); + } + + public setup({ httpClient }: SetupDependencies) { + const creationManagerSetup = this.indexPatternCreationManager.setup(httpClient); + creationManagerSetup.addCreationConfig(IndexPatternCreationConfig); + this.indexPatternListConfig.setup().addListConfig(IndexPatternListConfig); - home.featureCatalogue.register({ - id: 'index_patterns', - title: i18n.translate('management.indexPatternHeader', { - defaultMessage: 'Index Patterns', - }), - description: i18n.translate('management.indexPatternLabel', { - defaultMessage: - 'Manage the index patterns that help retrieve your data from Elasticsearch.', - }), - icon: 'indexPatternApp', - path: '/app/kibana#/management/kibana/index_patterns', - showOnHomePage: true, - category: FeatureCatalogueCategory.ADMIN, - }); + return { + creation: creationManagerSetup, + list: this.indexPatternListConfig.setup(), + }; + } + public start() { return { - creation, - list, + creation: this.indexPatternCreationManager.start(), + list: this.indexPatternListConfig.start(), }; } @@ -71,4 +63,5 @@ export class IndexPatternManagementService { } /** @internal */ -export type IndexPatternManagementSetup = ReturnType; +export type IndexPatternManagementServiceSetup = ReturnType; +export type IndexPatternManagementServiceStart = ReturnType; diff --git a/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/list/config.ts b/src/plugins/index_pattern_management/public/service/list/config.ts similarity index 87% rename from src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/list/config.ts rename to src/plugins/index_pattern_management/public/service/list/config.ts index dd4d77a681171..87c246e8913e5 100644 --- a/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/list/config.ts +++ b/src/plugins/index_pattern_management/public/service/list/config.ts @@ -33,9 +33,12 @@ export class IndexPatternListConfig { ? [ { key: 'default', - name: i18n.translate('management.editIndexPattern.list.defaultIndexPatternListName', { - defaultMessage: 'Default', - }), + name: i18n.translate( + 'indexPatternManagement.editIndexPattern.list.defaultIndexPatternListName', + { + defaultMessage: 'Default', + } + ), }, ] : []; diff --git a/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/list/index.ts b/src/plugins/index_pattern_management/public/service/list/index.ts similarity index 100% rename from src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/list/index.ts rename to src/plugins/index_pattern_management/public/service/list/index.ts diff --git a/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/list/manager.ts b/src/plugins/index_pattern_management/public/service/list/manager.ts similarity index 75% rename from src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/list/manager.ts rename to src/plugins/index_pattern_management/public/service/list/manager.ts index 73ca33ae914a9..3a2910a222cd7 100644 --- a/src/legacy/core_plugins/management/public/np_ready/services/index_pattern_management/list/manager.ts +++ b/src/plugins/index_pattern_management/public/service/list/manager.ts @@ -27,7 +27,7 @@ export class IndexPatternListManager { this.configs = []; } - public add(Config: typeof IndexPatternListConfig) { + private addListConfig(Config: typeof IndexPatternListConfig) { const config = new Config(); if (this.configs.findIndex(c => c.key === config.key) !== -1) { throw new Error(`${config.key} exists in IndexPatternListManager.`); @@ -35,7 +35,7 @@ export class IndexPatternListManager { this.configs.push(config); } - public getIndexPatternTags(indexPattern: IIndexPattern, isDefault: boolean) { + private getIndexPatternTags(indexPattern: IIndexPattern, isDefault: boolean) { return this.configs.reduce((tags: IndexPatternTag[], config) => { return config.getIndexPatternTags ? tags.concat(config.getIndexPatternTags(indexPattern, isDefault)) @@ -43,15 +43,25 @@ export class IndexPatternListManager { }, []); } - public getFieldInfo(indexPattern: IIndexPattern, field: IFieldType): string[] { + private getFieldInfo(indexPattern: IIndexPattern, field: IFieldType): string[] { return this.configs.reduce((info: string[], config) => { return config.getFieldInfo ? info.concat(config.getFieldInfo(indexPattern, field)) : info; }, []); } - public areScriptedFieldsEnabled(indexPattern: IIndexPattern): boolean { + private areScriptedFieldsEnabled(indexPattern: IIndexPattern): boolean { return this.configs.every(config => { return config.areScriptedFieldsEnabled ? config.areScriptedFieldsEnabled(indexPattern) : true; }); } + + setup = () => ({ + addListConfig: this.addListConfig.bind(this), + }); + + start = () => ({ + getIndexPatternTags: this.getIndexPatternTags.bind(this), + getFieldInfo: this.getFieldInfo.bind(this), + areScriptedFieldsEnabled: this.areScriptedFieldsEnabled.bind(this), + }); } diff --git a/src/plugins/kibana_react/kibana.json b/src/plugins/kibana_react/kibana.json new file mode 100644 index 0000000000000..0add1bee84ae0 --- /dev/null +++ b/src/plugins/kibana_react/kibana.json @@ -0,0 +1,5 @@ +{ + "id": "kibanaReact", + "version": "kibana", + "ui": true +} diff --git a/src/plugins/kibana_react/public/adapters/react_to_ui_component.ts b/src/plugins/kibana_react/public/adapters/react_to_ui_component.ts index b4007b30cf8ca..21bba92ada4c1 100644 --- a/src/plugins/kibana_react/public/adapters/react_to_ui_component.ts +++ b/src/plugins/kibana_react/public/adapters/react_to_ui_component.ts @@ -19,7 +19,7 @@ import { ComponentType, createElement as h } from 'react'; import { render as renderReact, unmountComponentAtNode } from 'react-dom'; -import { UiComponent, UiComponentInstance } from '../../../kibana_utils/common'; +import { UiComponent, UiComponentInstance } from '../../../kibana_utils/public'; /** * Transform a React component into a `UiComponent`. diff --git a/src/plugins/kibana_react/public/adapters/ui_to_react_component.test.tsx b/src/plugins/kibana_react/public/adapters/ui_to_react_component.test.tsx index 939d372b9997f..aefbd66e50fcf 100644 --- a/src/plugins/kibana_react/public/adapters/ui_to_react_component.test.tsx +++ b/src/plugins/kibana_react/public/adapters/ui_to_react_component.test.tsx @@ -19,7 +19,7 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; -import { UiComponent } from '../../../kibana_utils/common'; +import { UiComponent } from '../../../kibana_utils/public'; import { uiToReactComponent } from './ui_to_react_component'; import { reactToUiComponent } from './react_to_ui_component'; diff --git a/src/plugins/kibana_react/public/adapters/ui_to_react_component.ts b/src/plugins/kibana_react/public/adapters/ui_to_react_component.ts index 9b34880cf4fe3..ee99ea3672672 100644 --- a/src/plugins/kibana_react/public/adapters/ui_to_react_component.ts +++ b/src/plugins/kibana_react/public/adapters/ui_to_react_component.ts @@ -18,7 +18,7 @@ */ import { FC, createElement as h, useRef, useLayoutEffect, useMemo } from 'react'; -import { UiComponent, UiComponentInstance } from '../../../kibana_utils/common'; +import { UiComponent, UiComponentInstance } from '../../../kibana_utils/public'; /** * Transforms `UiComponent` into a React component. diff --git a/src/plugins/kibana_react/public/index.ts b/src/plugins/kibana_react/public/index.ts index e1689e38dbfe0..9ad9f14ac5659 100644 --- a/src/plugins/kibana_react/public/index.ts +++ b/src/plugins/kibana_react/public/index.ts @@ -31,3 +31,11 @@ export { Markdown, MarkdownSimple } from './markdown'; export { reactToUiComponent, uiToReactComponent } from './adapters'; export { useUrlTracker } from './use_url_tracker'; export { toMountPoint } from './util'; + +/** dummy plugin, we just want kibanaReact to have its own bundle */ +export function plugin() { + return new (class KibanaReactPlugin { + setup() {} + start() {} + })(); +} diff --git a/src/plugins/kibana_utils/kibana.json b/src/plugins/kibana_utils/kibana.json new file mode 100644 index 0000000000000..6fa39d82d1021 --- /dev/null +++ b/src/plugins/kibana_utils/kibana.json @@ -0,0 +1,5 @@ +{ + "id": "kibanaUtils", + "version": "kibana", + "ui": true +} diff --git a/src/plugins/kibana_utils/public/index.ts b/src/plugins/kibana_utils/public/index.ts index 1876e688c989a..2f139050e994a 100644 --- a/src/plugins/kibana_utils/public/index.ts +++ b/src/plugins/kibana_utils/public/index.ts @@ -19,7 +19,6 @@ export { calculateObjectHash, - createGetterSetter, defer, Defer, Get, @@ -31,6 +30,8 @@ export { UiComponent, UiComponentInstance, url, + createGetterSetter, + defaultFeedbackMessage, } from '../common'; export * from './core'; export * from './errors'; @@ -75,3 +76,11 @@ export { } from './state_sync'; export { removeQueryParam, redirectWhenMissing, ensureDefaultIndexPattern } from './history'; export { applyDiff } from './state_management/utils/diff_object'; + +/** dummy plugin, we just want kibanaUtils to have its own bundle */ +export function plugin() { + return new (class KibanaUtilsPlugin { + setup() {} + start() {} + })(); +} diff --git a/src/plugins/saved_objects/public/plugin.ts b/src/plugins/saved_objects/public/plugin.ts index 0f5773c00283e..7927238e12066 100644 --- a/src/plugins/saved_objects/public/plugin.ts +++ b/src/plugins/saved_objects/public/plugin.ts @@ -39,6 +39,7 @@ export class SavedObjectsPublicPlugin SavedObjectClass: createSavedObjectClass({ indexPatterns: data.indexPatterns, savedObjectsClient: core.savedObjects.client, + search: data.search, chrome: core.chrome, overlays: core.overlays, }), diff --git a/src/plugins/saved_objects/public/saved_object/helpers/apply_es_resp.ts b/src/plugins/saved_objects/public/saved_object/helpers/apply_es_resp.ts index 2e965eaf1989b..9776887b6d741 100644 --- a/src/plugins/saved_objects/public/saved_object/helpers/apply_es_resp.ts +++ b/src/plugins/saved_objects/public/saved_object/helpers/apply_es_resp.ts @@ -18,9 +18,8 @@ */ import _ from 'lodash'; import { EsResponse, SavedObject, SavedObjectConfig } from '../../types'; -import { parseSearchSource } from './parse_search_source'; import { expandShorthand, SavedObjectNotFound } from '../../../../kibana_utils/public'; -import { IndexPattern } from '../../../../data/public'; +import { DataPublicPluginStart, IndexPattern } from '../../../../data/public'; /** * A given response of and ElasticSearch containing a plain saved object is applied to the given @@ -29,13 +28,13 @@ import { IndexPattern } from '../../../../data/public'; export async function applyESResp( resp: EsResponse, savedObject: SavedObject, - config: SavedObjectConfig + config: SavedObjectConfig, + createSearchSource: DataPublicPluginStart['search']['createSearchSource'] ) { const mapping = expandShorthand(config.mapping); const esType = config.type || ''; savedObject._source = _.cloneDeep(resp._source); const injectReferences = config.injectReferences; - const hydrateIndexPattern = savedObject.hydrateIndexPattern!; if (typeof resp.found === 'boolean' && !resp.found) { throw new SavedObjectNotFound(esType, savedObject.id || ''); } @@ -64,13 +63,34 @@ export async function applyESResp( _.assign(savedObject, savedObject._source); savedObject.lastSavedTitle = savedObject.title; - await parseSearchSource(savedObject, esType, meta.searchSourceJSON, resp.references); - await hydrateIndexPattern(); + if (config.searchSource) { + try { + savedObject.searchSource = await createSearchSource(meta.searchSourceJSON, resp.references); + } catch (error) { + if ( + error.constructor.name === 'SavedObjectNotFound' && + error.savedObjectType === 'index-pattern' + ) { + // if parsing the search source fails because the index pattern wasn't found, + // remember the reference - this is required for error handling on legacy imports + savedObject.unresolvedIndexPatternReference = { + name: 'kibanaSavedObjectMeta.searchSourceJSON.index', + id: JSON.parse(meta.searchSourceJSON).index, + type: 'index-pattern', + }; + } + + throw error; + } + } + if (injectReferences && resp.references && resp.references.length > 0) { injectReferences(savedObject, resp.references); } + if (typeof config.afterESResp === 'function') { savedObject = await config.afterESResp(savedObject); } + return savedObject; } diff --git a/src/plugins/saved_objects/public/saved_object/helpers/build_saved_object.ts b/src/plugins/saved_objects/public/saved_object/helpers/build_saved_object.ts index b9043890e2775..e8faef4e9e040 100644 --- a/src/plugins/saved_objects/public/saved_object/helpers/build_saved_object.ts +++ b/src/plugins/saved_objects/public/saved_object/helpers/build_saved_object.ts @@ -81,7 +81,8 @@ export function buildSavedObject( */ savedObject.init = _.once(() => intializeSavedObject(savedObject, savedObjectsClient, config)); - savedObject.applyESResp = (resp: EsResponse) => applyESResp(resp, savedObject, config); + savedObject.applyESResp = (resp: EsResponse) => + applyESResp(resp, savedObject, config, services.search.createSearchSource); /** * Serialize this object diff --git a/src/plugins/saved_objects/public/saved_object/helpers/hydrate_index_pattern.ts b/src/plugins/saved_objects/public/saved_object/helpers/hydrate_index_pattern.ts index b55538e4073ba..84275cf35befb 100644 --- a/src/plugins/saved_objects/public/saved_object/helpers/hydrate_index_pattern.ts +++ b/src/plugins/saved_objects/public/saved_object/helpers/hydrate_index_pattern.ts @@ -31,25 +31,19 @@ export async function hydrateIndexPattern( indexPatterns: IndexPatternsContract, config: SavedObjectConfig ) { - const clearSavedIndexPattern = !!config.clearSavedIndexPattern; const indexPattern = config.indexPattern; if (!savedObject.searchSource) { return null; } - if (clearSavedIndexPattern) { - savedObject.searchSource!.setField('index', undefined); - return null; - } - - const index = id || indexPattern || savedObject.searchSource!.getOwnField('index'); + const index = id || indexPattern || savedObject.searchSource.getOwnField('index'); if (typeof index !== 'string' || !index) { return null; } const indexObj = await indexPatterns.get(index); - savedObject.searchSource!.setField('index', indexObj); + savedObject.searchSource.setField('index', indexObj); return indexObj; } diff --git a/src/plugins/saved_objects/public/saved_object/helpers/parse_search_source.ts b/src/plugins/saved_objects/public/saved_object/helpers/parse_search_source.ts deleted file mode 100644 index cdb191f9e7df8..0000000000000 --- a/src/plugins/saved_objects/public/saved_object/helpers/parse_search_source.ts +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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. - */ -import _ from 'lodash'; -import { migrateLegacyQuery } from '../../../../kibana_legacy/public'; -import { SavedObject } from '../../types'; -import { InvalidJSONProperty } from '../../../../kibana_utils/public'; - -export function parseSearchSource( - savedObject: SavedObject, - esType: string, - searchSourceJson: string, - references: any[] -) { - if (!savedObject.searchSource) return; - - // if we have a searchSource, set its values based on the searchSourceJson field - let searchSourceValues: Record; - try { - searchSourceValues = JSON.parse(searchSourceJson); - } catch (e) { - throw new InvalidJSONProperty( - `Invalid JSON in ${esType} "${savedObject.id}". ${e.message} JSON: ${searchSourceJson}` - ); - } - - // This detects a scenario where documents with invalid JSON properties have been imported into the saved object index. - // (This happened in issue #20308) - if (!searchSourceValues || typeof searchSourceValues !== 'object') { - throw new InvalidJSONProperty(`Invalid searchSourceJSON in ${esType} "${savedObject.id}".`); - } - - // Inject index id if a reference is saved - if (searchSourceValues.indexRefName) { - const reference = references.find( - (ref: Record) => ref.name === searchSourceValues.indexRefName - ); - if (!reference) { - throw new Error( - `Could not find reference for ${ - searchSourceValues.indexRefName - } on ${savedObject.getEsType()} ${savedObject.id}` - ); - } - searchSourceValues.index = reference.id; - delete searchSourceValues.indexRefName; - } - - if (searchSourceValues.filter) { - searchSourceValues.filter.forEach((filterRow: any) => { - if (!filterRow.meta || !filterRow.meta.indexRefName) { - return; - } - const reference = references.find((ref: any) => ref.name === filterRow.meta.indexRefName); - if (!reference) { - throw new Error( - `Could not find reference for ${ - filterRow.meta.indexRefName - } on ${savedObject.getEsType()}` - ); - } - filterRow.meta.index = reference.id; - delete filterRow.meta.indexRefName; - }); - } - - const searchSourceFields = savedObject.searchSource.getFields(); - const fnProps = _.transform( - searchSourceFields, - function(dynamic: Record, val: any, name: string | undefined) { - if (_.isFunction(val) && name) dynamic[name] = val; - }, - {} - ); - - savedObject.searchSource.setFields(_.defaults(searchSourceValues, fnProps)); - const query = savedObject.searchSource.getOwnField('query'); - - if (typeof query !== 'undefined') { - savedObject.searchSource.setField('query', migrateLegacyQuery(query)); - } -} diff --git a/src/plugins/saved_objects/public/saved_object/helpers/serialize_saved_object.ts b/src/plugins/saved_objects/public/saved_object/helpers/serialize_saved_object.ts index 8a020ca03aea3..78f9eeb8b5fb1 100644 --- a/src/plugins/saved_objects/public/saved_object/helpers/serialize_saved_object.ts +++ b/src/plugins/saved_objects/public/saved_object/helpers/serialize_saved_object.ts @@ -17,7 +17,6 @@ * under the License. */ import _ from 'lodash'; -import angular from 'angular'; import { SavedObject, SavedObjectConfig } from '../../types'; import { expandShorthand } from '../../../../kibana_utils/public'; @@ -41,57 +40,16 @@ export function serializeSavedObject(savedObject: SavedObject, config: SavedObje }); if (savedObject.searchSource) { - let searchSourceFields: Record = _.omit(savedObject.searchSource.getFields(), [ - 'sort', - 'size', - ]); - if (searchSourceFields.index) { - // searchSourceFields.index will normally be an IndexPattern, but can be a string in two scenarios: - // (1) `init()` (and by extension `hydrateIndexPattern()`) hasn't been called on Saved Object - // (2) The IndexPattern doesn't exist, so we fail to resolve it in `hydrateIndexPattern()` - const indexId = - typeof searchSourceFields.index === 'string' - ? searchSourceFields.index - : searchSourceFields.index.id; - const refName = 'kibanaSavedObjectMeta.searchSourceJSON.index'; - references.push({ - name: refName, - type: 'index-pattern', - id: indexId, - }); - searchSourceFields = { - ...searchSourceFields, - indexRefName: refName, - index: undefined, - }; - } - if (searchSourceFields.filter) { - searchSourceFields = { - ...searchSourceFields, - filter: searchSourceFields.filter.map((filterRow: any, i: number) => { - if (!filterRow.meta || !filterRow.meta.index) { - return filterRow; - } - const refName = `kibanaSavedObjectMeta.searchSourceJSON.filter[${i}].meta.index`; - references.push({ - name: refName, - type: 'index-pattern', - id: filterRow.meta.index, - }); - return { - ...filterRow, - meta: { - ...filterRow.meta, - indexRefName: refName, - index: undefined, - }, - }; - }), - }; - } - attributes.kibanaSavedObjectMeta = { - searchSourceJSON: angular.toJson(searchSourceFields), - }; + const { + searchSourceJSON, + references: searchSourceReferences, + } = savedObject.searchSource.serialize(); + attributes.kibanaSavedObjectMeta = { searchSourceJSON }; + references.push(...searchSourceReferences); + } + + if (savedObject.unresolvedIndexPatternReference) { + references.push(savedObject.unresolvedIndexPatternReference); } return { attributes, references }; diff --git a/src/plugins/saved_objects/public/saved_object/saved_object.test.ts b/src/plugins/saved_objects/public/saved_object/saved_object.test.ts index 08389e9e3c97f..60c66f84080b2 100644 --- a/src/plugins/saved_objects/public/saved_object/saved_object.test.ts +++ b/src/plugins/saved_objects/public/saved_object/saved_object.test.ts @@ -103,9 +103,11 @@ describe('Saved Object', () => { } beforeEach(() => { + (dataStartMock.search.createSearchSource as jest.Mock).mockReset(); SavedObjectClass = createSavedObjectClass({ savedObjectsClient: savedObjectsClientStub, indexPatterns: dataStartMock.indexPatterns, + search: dataStartMock.search, } as SavedObjectKibanaServices); }); @@ -269,7 +271,7 @@ describe('Saved Object', () => { ); }); - it('when index exists in searchSourceJSON', () => { + it('when search source references saved object', () => { const id = '123'; stubESResponse(getMockedDocResponse(id)); return createInitializedSavedObject({ type: 'dashboard', searchSource: true }).then( @@ -409,18 +411,17 @@ describe('Saved Object', () => { }); }); - it('throws error invalid JSON is detected', async () => { + it('forwards thrown exceptions from createSearchSource', async () => { + (dataStartMock.search.createSearchSource as jest.Mock).mockImplementation(() => { + throw new InvalidJSONProperty(''); + }); const savedObject = await createInitializedSavedObject({ type: 'dashboard', searchSource: true, }); const response = { found: true, - _source: { - kibanaSavedObjectMeta: { - searchSourceJSON: '"{\\n \\"filter\\": []\\n}"', - }, - }, + _source: {}, }; try { @@ -586,23 +587,24 @@ describe('Saved Object', () => { }); }); - it('injects references from searchSourceJSON', async () => { + it('passes references to search source parsing function', async () => { const savedObject = new SavedObjectClass({ type: 'dashboard', searchSource: true }); return savedObject.init!().then(() => { + const searchSourceJSON = JSON.stringify({ + indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.index', + filter: [ + { + meta: { + indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index', + }, + }, + ], + }); const response = { found: true, _source: { kibanaSavedObjectMeta: { - searchSourceJSON: JSON.stringify({ - indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.index', - filter: [ - { - meta: { - indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index', - }, - }, - ], - }), + searchSourceJSON, }, }, references: [ @@ -619,16 +621,10 @@ describe('Saved Object', () => { ], }; savedObject.applyESResp(response); - expect(savedObject.searchSource!.getFields()).toEqual({ - index: 'my-index-1', - filter: [ - { - meta: { - index: 'my-index-2', - }, - }, - ], - }); + expect(dataStartMock.search.createSearchSource).toBeCalledWith( + searchSourceJSON, + response.references + ); }); }); }); diff --git a/src/plugins/saved_objects/public/types.ts b/src/plugins/saved_objects/public/types.ts index 99088df84ec36..3184038040952 100644 --- a/src/plugins/saved_objects/public/types.ts +++ b/src/plugins/saved_objects/public/types.ts @@ -24,7 +24,12 @@ import { SavedObjectAttributes, SavedObjectReference, } from 'kibana/public'; -import { IIndexPattern, IndexPatternsContract, ISearchSource } from '../../data/public'; +import { + DataPublicPluginStart, + IIndexPattern, + IndexPatternsContract, + ISearchSource, +} from '../../data/public'; export interface SavedObject { _serialize: () => { attributes: SavedObjectAttributes; references: SavedObjectReference[] }; @@ -49,6 +54,7 @@ export interface SavedObject { searchSource?: ISearchSource; showInRecentlyAccessed: boolean; title: string; + unresolvedIndexPatternReference?: SavedObjectReference; } export interface SavedObjectSaveOpts { @@ -65,6 +71,7 @@ export interface SavedObjectCreationOpts { export interface SavedObjectKibanaServices { savedObjectsClient: SavedObjectsClientContract; indexPatterns: IndexPatternsContract; + search: DataPublicPluginStart['search']; chrome: ChromeStart; overlays: OverlayStart; } @@ -72,7 +79,6 @@ export interface SavedObjectKibanaServices { export interface SavedObjectConfig { // is only used by visualize afterESResp?: (savedObject: SavedObject) => Promise; - clearSavedIndexPattern?: boolean; defaults?: any; extractReferences?: (opts: { attributes: SavedObjectAttributes; diff --git a/src/plugins/share/public/components/url_panel_content.tsx b/src/plugins/share/public/components/url_panel_content.tsx index 2b77b6f4592a8..2b1159be89003 100644 --- a/src/plugins/share/public/components/url_panel_content.tsx +++ b/src/plugins/share/public/components/url_panel_content.tsx @@ -166,7 +166,7 @@ export class UrlPanelContent extends Component { // Get the application route, after the hash, and remove the #. const parsedAppUrl = parseUrl(parsedUrl.hash.slice(1), true); - return formatUrl({ + let formattedUrl = formatUrl({ protocol: parsedUrl.protocol, auth: parsedUrl.auth, host: parsedUrl.host, @@ -180,6 +180,11 @@ export class UrlPanelContent extends Component { }, }), }); + if (this.props.isEmbedded) { + formattedUrl = this.makeUrlEmbeddable(url); + } + + return formattedUrl; }; private getSnapshotUrl = () => { diff --git a/src/plugins/telemetry/common/constants.ts b/src/plugins/telemetry/common/constants.ts index babd009143c5e..fd32862896528 100644 --- a/src/plugins/telemetry/common/constants.ts +++ b/src/plugins/telemetry/common/constants.ts @@ -80,3 +80,14 @@ export const APPLICATION_USAGE_TYPE = 'application_usage'; * The type name used within the Monitoring index to publish management stats. */ export const KIBANA_STACK_MANAGEMENT_STATS_TYPE = 'stack_management'; + +/** + * The type name used to publish Kibana usage stats. + * NOTE: this string shows as-is in the stats API as a field name for the kibana usage stats + */ +export const KIBANA_USAGE_TYPE = 'kibana'; + +/** + * The type name used to publish Kibana usage stats in the formatted as bulk. + */ +export const KIBANA_STATS_TYPE = 'kibana_stats'; diff --git a/src/plugins/telemetry/server/collectors/index.ts b/src/plugins/telemetry/server/collectors/index.ts index 6eeda080bb3ab..a874f8dd6202c 100644 --- a/src/plugins/telemetry/server/collectors/index.ts +++ b/src/plugins/telemetry/server/collectors/index.ts @@ -22,3 +22,5 @@ export { registerUiMetricUsageCollector } from './ui_metric'; export { registerTelemetryPluginUsageCollector } from './telemetry_plugin'; export { registerManagementUsageCollector } from './management'; export { registerApplicationUsageCollector } from './application_usage'; +export { registerKibanaUsageCollector } from './kibana'; +export { registerOpsStatsCollector } from './ops_stats'; diff --git a/src/plugins/telemetry/server/collectors/kibana/get_saved_object_counts.test.ts b/src/plugins/telemetry/server/collectors/kibana/get_saved_object_counts.test.ts new file mode 100644 index 0000000000000..a7681e1766427 --- /dev/null +++ b/src/plugins/telemetry/server/collectors/kibana/get_saved_object_counts.test.ts @@ -0,0 +1,61 @@ +/* + * 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. + */ + +import { getSavedObjectsCounts } from './get_saved_object_counts'; + +describe('getSavedObjectsCounts', () => { + test('Get all the saved objects equal to 0 because no results were found', async () => { + const callCluster = jest.fn(() => ({})); + + const results = await getSavedObjectsCounts(callCluster as any, '.kibana'); + expect(results).toStrictEqual({ + dashboard: { total: 0 }, + visualization: { total: 0 }, + search: { total: 0 }, + index_pattern: { total: 0 }, + graph_workspace: { total: 0 }, + timelion_sheet: { total: 0 }, + }); + }); + + test('Merge the zeros with the results', async () => { + const callCluster = jest.fn(() => ({ + aggregations: { + types: { + buckets: [ + { key: 'dashboard', doc_count: 1 }, + { key: 'timelion-sheet', doc_count: 2 }, + { key: 'index-pattern', value: 2 }, // Malformed on purpose + { key: 'graph_workspace', doc_count: 3 }, // already snake_cased + ], + }, + }, + })); + + const results = await getSavedObjectsCounts(callCluster as any, '.kibana'); + expect(results).toStrictEqual({ + dashboard: { total: 1 }, + visualization: { total: 0 }, + search: { total: 0 }, + index_pattern: { total: 0 }, + graph_workspace: { total: 3 }, + timelion_sheet: { total: 2 }, + }); + }); +}); diff --git a/src/plugins/telemetry/server/collectors/kibana/get_saved_object_counts.ts b/src/plugins/telemetry/server/collectors/kibana/get_saved_object_counts.ts new file mode 100644 index 0000000000000..804c8b0ed2026 --- /dev/null +++ b/src/plugins/telemetry/server/collectors/kibana/get_saved_object_counts.ts @@ -0,0 +1,82 @@ +/* + * 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. + */ + +/** + * Moved from /x-pack/plugins/monitoring/server/kibana_monitoring/collectors/get_kibana_usage_collector.ts + * + * The PR https://github.com/elastic/kibana/pull/62665 proved what the issue https://github.com/elastic/kibana/issues/58249 + * was claiming: the structure and payload for common telemetry bits differs between Monitoring and OSS/X-Pack collections. + * + * Unifying this logic from Monitoring that makes sense to have in OSS here and we will import it on the monitoring side to reuse it. + */ + +import { snakeCase } from 'lodash'; +import { APICaller } from 'kibana/server'; + +const TYPES = [ + 'dashboard', + 'visualization', + 'search', + 'index-pattern', + 'graph-workspace', + 'timelion-sheet', +]; + +export interface KibanaSavedObjectCounts { + [pluginName: string]: { + total: number; + }; +} + +export async function getSavedObjectsCounts( + callCluster: APICaller, + kibanaIndex: string // Typically '.kibana'. We might need a way to obtain it from the SavedObjects client (or the SavedObjects client to provide a way to run aggregations?) +): Promise { + const savedObjectCountSearchParams = { + index: kibanaIndex, + ignoreUnavailable: true, + filterPath: 'aggregations.types.buckets', + body: { + size: 0, + query: { + terms: { type: TYPES }, + }, + aggs: { + types: { + terms: { field: 'type', size: TYPES.length }, + }, + }, + }, + }; + const resp = await callCluster('search', savedObjectCountSearchParams); + const buckets: Array<{ key: string; doc_count: number }> = + resp.aggregations?.types?.buckets || []; + + // Initialise the object with all zeros for all the types + const allZeros: KibanaSavedObjectCounts = TYPES.reduce( + (acc, type) => ({ ...acc, [snakeCase(type)]: { total: 0 } }), + {} + ); + + // Add the doc_count from each bucket + return buckets.reduce( + (acc, { key, doc_count: total }) => (total ? { ...acc, [snakeCase(key)]: { total } } : acc), + allZeros + ); +} diff --git a/src/plugins/telemetry/server/collectors/kibana/index.test.ts b/src/plugins/telemetry/server/collectors/kibana/index.test.ts new file mode 100644 index 0000000000000..91ede686ded3d --- /dev/null +++ b/src/plugins/telemetry/server/collectors/kibana/index.test.ts @@ -0,0 +1,76 @@ +/* + * 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. + */ + +import { UsageCollectionSetup } from '../../../../../plugins/usage_collection/server'; +import { pluginInitializerContextConfigMock } from '../../../../../core/server/mocks'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { CollectorOptions } from '../../../../../plugins/usage_collection/server/collector/collector'; + +import { registerKibanaUsageCollector } from './'; + +describe('telemetry_kibana', () => { + let collector: CollectorOptions; + + const usageCollectionMock: jest.Mocked = { + makeUsageCollector: jest.fn().mockImplementation(config => (collector = config)), + registerCollector: jest.fn(), + } as any; + + const legacyConfig$ = pluginInitializerContextConfigMock({}).legacy.globalConfig$; + const callCluster = jest.fn().mockImplementation(() => ({})); + + beforeAll(() => registerKibanaUsageCollector(usageCollectionMock, legacyConfig$)); + afterAll(() => jest.clearAllTimers()); + + test('registered collector is set', () => { + expect(collector).not.toBeUndefined(); + expect(collector.type).toBe('kibana'); + }); + + test('fetch', async () => { + expect(await collector.fetch(callCluster)).toStrictEqual({ + index: '.kibana-tests', + dashboard: { total: 0 }, + visualization: { total: 0 }, + search: { total: 0 }, + index_pattern: { total: 0 }, + graph_workspace: { total: 0 }, + timelion_sheet: { total: 0 }, + }); + }); + + test('formatForBulkUpload', async () => { + const resultFromFetch = { + index: '.kibana-tests', + dashboard: { total: 0 }, + visualization: { total: 0 }, + search: { total: 0 }, + index_pattern: { total: 0 }, + graph_workspace: { total: 0 }, + timelion_sheet: { total: 0 }, + }; + + expect(collector.formatForBulkUpload!(resultFromFetch)).toStrictEqual({ + type: 'kibana_stats', + payload: { + usage: resultFromFetch, + }, + }); + }); +}); diff --git a/src/legacy/server/status/collectors/index.js b/src/plugins/telemetry/server/collectors/kibana/index.ts similarity index 91% rename from src/legacy/server/status/collectors/index.js rename to src/plugins/telemetry/server/collectors/kibana/index.ts index 92d9e601bbb35..695d972346b8a 100644 --- a/src/legacy/server/status/collectors/index.js +++ b/src/plugins/telemetry/server/collectors/kibana/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { registerOpsStatsCollector } from './get_ops_stats_collector'; +export { registerKibanaUsageCollector } from './kibana_usage_collector'; diff --git a/src/plugins/telemetry/server/collectors/kibana/kibana_usage_collector.ts b/src/plugins/telemetry/server/collectors/kibana/kibana_usage_collector.ts new file mode 100644 index 0000000000000..ccf6f7b1033c9 --- /dev/null +++ b/src/plugins/telemetry/server/collectors/kibana/kibana_usage_collector.ts @@ -0,0 +1,65 @@ +/* + * 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. + */ + +import { Observable } from 'rxjs'; +import { take } from 'rxjs/operators'; +import { SharedGlobalConfig } from 'kibana/server'; +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { KIBANA_STATS_TYPE, KIBANA_USAGE_TYPE } from '../../../common/constants'; +import { getSavedObjectsCounts } from './get_saved_object_counts'; + +export function getKibanaUsageCollector( + usageCollection: UsageCollectionSetup, + legacyConfig$: Observable +) { + return usageCollection.makeUsageCollector({ + type: KIBANA_USAGE_TYPE, + isReady: () => true, + async fetch(callCluster) { + const { + kibana: { index }, + } = await legacyConfig$.pipe(take(1)).toPromise(); + return { + index, + ...(await getSavedObjectsCounts(callCluster, index)), + }; + }, + + /* + * Format the response data into a model for internal upload + * 1. Make this data part of the "kibana_stats" type + * 2. Organize the payload in the usage namespace of the data payload (usage.index, etc) + */ + formatForBulkUpload: result => { + return { + type: KIBANA_STATS_TYPE, + payload: { + usage: result, + }, + }; + }, + }); +} + +export function registerKibanaUsageCollector( + usageCollection: UsageCollectionSetup, + legacyConfig$: Observable +) { + usageCollection.registerCollector(getKibanaUsageCollector(usageCollection, legacyConfig$)); +} diff --git a/src/plugins/telemetry/server/collectors/ops_stats/__snapshots__/index.test.ts.snap b/src/plugins/telemetry/server/collectors/ops_stats/__snapshots__/index.test.ts.snap new file mode 100644 index 0000000000000..678237ffb6ea2 --- /dev/null +++ b/src/plugins/telemetry/server/collectors/ops_stats/__snapshots__/index.test.ts.snap @@ -0,0 +1,43 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`telemetry_ops_stats should return something when there is a metric 1`] = ` +Object { + "concurrent_connections": 20, + "os": Object { + "load": Object { + "15m": 3, + "1m": 0.5, + "5m": 1, + }, + "memory": Object { + "free_in_bytes": 10, + "total_in_bytes": 10, + "used_in_bytes": 10, + }, + "platform": "darwin", + "platformRelease": "test", + "uptime_in_millis": 1000, + }, + "process": Object { + "event_loop_delay": 10, + "memory": Object { + "heap": Object { + "size_limit": 0, + "total_in_bytes": 0, + "used_in_bytes": 0, + }, + "resident_set_size_in_bytes": 0, + }, + "uptime_in_millis": 1000, + }, + "requests": Object { + "disconnects": 10, + "total": 100, + }, + "response_times": Object { + "average": 100, + "max": 200, + }, + "timestamp": Any, +} +`; diff --git a/src/plugins/telemetry/server/collectors/ops_stats/index.test.ts b/src/plugins/telemetry/server/collectors/ops_stats/index.test.ts new file mode 100644 index 0000000000000..92e0e40776eb8 --- /dev/null +++ b/src/plugins/telemetry/server/collectors/ops_stats/index.test.ts @@ -0,0 +1,132 @@ +/* + * 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. + */ + +import { Subject } from 'rxjs'; +import { UsageCollectionSetup } from '../../../../../plugins/usage_collection/server'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { CollectorOptions } from '../../../../../plugins/usage_collection/server/collector/collector'; + +import { registerOpsStatsCollector } from './'; +import { OpsMetrics } from '../../../../../core/server'; + +describe('telemetry_ops_stats', () => { + let collector: CollectorOptions; + + const usageCollectionMock: jest.Mocked = { + makeStatsCollector: jest.fn().mockImplementation(config => (collector = config)), + registerCollector: jest.fn(), + } as any; + + const metrics$ = new Subject(); + const callCluster = jest.fn(); + + const metric: OpsMetrics = { + process: { + memory: { + heap: { + total_in_bytes: 0, + used_in_bytes: 0, + size_limit: 0, + }, + resident_set_size_in_bytes: 0, + }, + event_loop_delay: 10, + pid: 10, + uptime_in_millis: 1000, + }, + os: { + platform: 'darwin', + platformRelease: 'test', + load: { + '1m': 0.5, + '5m': 1, + '15m': 3, + }, + memory: { + total_in_bytes: 10, + free_in_bytes: 10, + used_in_bytes: 10, + }, + uptime_in_millis: 1000, + }, + response_times: { avg_in_millis: 100, max_in_millis: 200 }, + requests: { + disconnects: 10, + total: 100, + statusCodes: { 200: 100 }, + }, + concurrent_connections: 20, + }; + + beforeAll(() => registerOpsStatsCollector(usageCollectionMock, metrics$)); + afterAll(() => jest.clearAllTimers()); + + test('registered collector is set', () => { + expect(collector).not.toBeUndefined(); + expect(collector.type).toBe('kibana_stats'); + }); + + test('isReady should return false because no metrics have been provided yet', () => { + expect(collector.isReady()).toBe(false); + }); + + test('should return something when there is a metric', async () => { + metrics$.next(metric); + expect(collector.isReady()).toBe(true); + expect(await collector.fetch(callCluster)).toMatchSnapshot({ + concurrent_connections: 20, + os: { + load: { + '15m': 3, + '1m': 0.5, + '5m': 1, + }, + memory: { + free_in_bytes: 10, + total_in_bytes: 10, + used_in_bytes: 10, + }, + platform: 'darwin', + platformRelease: 'test', + uptime_in_millis: 1000, + }, + process: { + event_loop_delay: 10, + memory: { + heap: { + size_limit: 0, + total_in_bytes: 0, + used_in_bytes: 0, + }, + resident_set_size_in_bytes: 0, + }, + uptime_in_millis: 1000, + }, + requests: { + disconnects: 10, + total: 100, + }, + response_times: { + average: 100, + max: 200, + }, + timestamp: expect.any(String), + }); + }); +}); diff --git a/src/legacy/core_plugins/management/public/np_ready/services/index.ts b/src/plugins/telemetry/server/collectors/ops_stats/index.ts similarity index 92% rename from src/legacy/core_plugins/management/public/np_ready/services/index.ts rename to src/plugins/telemetry/server/collectors/ops_stats/index.ts index 9df010223542b..443a25749d200 100644 --- a/src/legacy/core_plugins/management/public/np_ready/services/index.ts +++ b/src/plugins/telemetry/server/collectors/ops_stats/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export * from './index_pattern_management'; +export { registerOpsStatsCollector } from './ops_stats_collector'; diff --git a/src/plugins/telemetry/server/collectors/ops_stats/ops_stats_collector.ts b/src/plugins/telemetry/server/collectors/ops_stats/ops_stats_collector.ts new file mode 100644 index 0000000000000..4967e20006ddd --- /dev/null +++ b/src/plugins/telemetry/server/collectors/ops_stats/ops_stats_collector.ts @@ -0,0 +1,71 @@ +/* + * 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. + */ + +import { Observable } from 'rxjs'; +import { cloneDeep } from 'lodash'; +import moment from 'moment'; +import { OpsMetrics } from 'kibana/server'; +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { KIBANA_STATS_TYPE } from '../../../common/constants'; + +interface OpsStatsMetrics extends Omit { + timestamp: string; + response_times: { + average: number; + max: number; + }; +} + +/** + * Initialize a collector for Kibana Ops Stats + */ +export function getOpsStatsCollector( + usageCollection: UsageCollectionSetup, + metrics$: Observable +) { + let lastMetrics: OpsStatsMetrics | null = null; + metrics$.subscribe(_metrics => { + const metrics = cloneDeep(_metrics); + // Ensure we only include the same data that Metricbeat collection would get + delete metrics.process.pid; + const responseTimes = { + average: metrics.response_times.avg_in_millis, + max: metrics.response_times.max_in_millis, + }; + delete metrics.requests.statusCodes; + lastMetrics = { + ...metrics, + response_times: responseTimes, + timestamp: moment.utc().toISOString(), + }; + }); + + return usageCollection.makeStatsCollector({ + type: KIBANA_STATS_TYPE, + isReady: () => !!lastMetrics, + fetch: () => lastMetrics, + }); +} + +export function registerOpsStatsCollector( + usageCollection: UsageCollectionSetup, + metrics$: Observable +) { + usageCollection.registerCollector(getOpsStatsCollector(usageCollection, metrics$)); +} diff --git a/src/plugins/telemetry/server/plugin.ts b/src/plugins/telemetry/server/plugin.ts index 77036b4ea7ddc..1df6a665e4d76 100644 --- a/src/plugins/telemetry/server/plugin.ts +++ b/src/plugins/telemetry/server/plugin.ts @@ -32,6 +32,8 @@ import { SavedObjectsClient, Plugin, Logger, + SharedGlobalConfig, + MetricsServiceSetup, } from '../../../core/server'; import { registerRoutes } from './routes'; import { registerCollection } from './telemetry_collection'; @@ -41,6 +43,8 @@ import { registerTelemetryPluginUsageCollector, registerManagementUsageCollector, registerApplicationUsageCollector, + registerKibanaUsageCollector, + registerOpsStatsCollector, } from './collectors'; import { TelemetryConfigType } from './config'; import { FetcherTask } from './fetcher'; @@ -61,6 +65,7 @@ export class TelemetryPlugin implements Plugin { private readonly logger: Logger; private readonly currentKibanaVersion: string; private readonly config$: Observable; + private readonly legacyConfig$: Observable; private readonly isDev: boolean; private readonly fetcherTask: FetcherTask; private savedObjectsClient?: ISavedObjectsRepository; @@ -71,6 +76,7 @@ export class TelemetryPlugin implements Plugin { this.isDev = initializerContext.env.mode.dev; this.currentKibanaVersion = initializerContext.env.packageInfo.version; this.config$ = initializerContext.config.create(); + this.legacyConfig$ = initializerContext.config.legacy.globalConfig$; this.fetcherTask = new FetcherTask({ ...initializerContext, logger: this.logger, @@ -78,15 +84,15 @@ export class TelemetryPlugin implements Plugin { } public async setup( - core: CoreSetup, + { elasticsearch, http, savedObjects, metrics }: CoreSetup, { usageCollection, telemetryCollectionManager }: TelemetryPluginsSetup ) { const currentKibanaVersion = this.currentKibanaVersion; const config$ = this.config$; const isDev = this.isDev; - registerCollection(telemetryCollectionManager, core.elasticsearch.dataClient); - const router = core.http.createRouter(); + registerCollection(telemetryCollectionManager, elasticsearch.dataClient); + const router = http.createRouter(); registerRoutes({ config$, @@ -96,8 +102,8 @@ export class TelemetryPlugin implements Plugin { telemetryCollectionManager, }); - this.registerMappings(opts => core.savedObjects.registerType(opts)); - this.registerUsageCollectors(usageCollection, opts => core.savedObjects.registerType(opts)); + this.registerMappings(opts => savedObjects.registerType(opts)); + this.registerUsageCollectors(usageCollection, metrics, opts => savedObjects.registerType(opts)); } public async start(core: CoreStart, { telemetryCollectionManager }: TelemetryPluginsStart) { @@ -153,11 +159,14 @@ export class TelemetryPlugin implements Plugin { private registerUsageCollectors( usageCollection: UsageCollectionSetup, + metrics: MetricsServiceSetup, registerType: SavedObjectsRegisterType ) { const getSavedObjectsClient = () => this.savedObjectsClient; const getUiSettingsClient = () => this.uiSettingsClient; + registerOpsStatsCollector(usageCollection, metrics.getOpsMetrics$()); + registerKibanaUsageCollector(usageCollection, this.legacyConfig$); registerTelemetryPluginUsageCollector(usageCollection, { currentKibanaVersion: this.currentKibanaVersion, config$: this.config$, diff --git a/src/plugins/telemetry/server/telemetry_collection/get_kibana.ts b/src/plugins/telemetry/server/telemetry_collection/get_kibana.ts index 86c6731e11d37..a17f1b8232a22 100644 --- a/src/plugins/telemetry/server/telemetry_collection/get_kibana.ts +++ b/src/plugins/telemetry/server/telemetry_collection/get_kibana.ts @@ -55,6 +55,10 @@ export function handleKibanaStats( ...kibanaStats.os, }; const formattedOsStats = Object.entries(os).reduce((acc, [key, value]) => { + if (typeof value !== 'string') { + // There are new fields reported now from the "os" property like "load", "memory", etc. They are objects. + return acc; + } return { ...acc, [`${key}s`]: [{ [key]: value, count: 1 }], diff --git a/src/plugins/ui_actions/public/actions/action.ts b/src/plugins/ui_actions/public/actions/action.ts index 2b2fc004a84c6..f532c2c8aa219 100644 --- a/src/plugins/ui_actions/public/actions/action.ts +++ b/src/plugins/ui_actions/public/actions/action.ts @@ -17,7 +17,7 @@ * under the License. */ -import { UiComponent } from 'src/plugins/kibana_utils/common'; +import { UiComponent } from 'src/plugins/kibana_utils/public'; import { ActionType, ActionContextMapping } from '../types'; export type ActionByType = Action; diff --git a/src/plugins/ui_actions/public/actions/action_definition.ts b/src/plugins/ui_actions/public/actions/action_definition.ts index c590cf8f34ee0..3eaa13572a826 100644 --- a/src/plugins/ui_actions/public/actions/action_definition.ts +++ b/src/plugins/ui_actions/public/actions/action_definition.ts @@ -17,7 +17,7 @@ * under the License. */ -import { UiComponent } from 'src/plugins/kibana_utils/common'; +import { UiComponent } from 'src/plugins/kibana_utils/public'; import { ActionType, ActionContextMapping } from '../types'; export interface ActionDefinition { diff --git a/src/plugins/vis_default_editor/README.md b/src/plugins/vis_default_editor/README.md new file mode 100644 index 0000000000000..8607dfe486ace --- /dev/null +++ b/src/plugins/vis_default_editor/README.md @@ -0,0 +1,16 @@ +# Visualization Deafult Editor plugin + +The default editor is used in most primary visualizations, e.x. `Area`, `Data table`, `Pie`, etc. +It acts as a container for a particular visualization and options tabs. Contains the default "Data" tab in `public/components/sidebar/data_tab.tsx`. +The plugin exposes the static `DefaultEditorController` class to consume. + +```ts +import { DefaultEditorController } from '../../vis_default_editor/public'; + +const editor = new DefaultEditorController( + element, + vis, + eventEmitter, + embeddableHandler +); +``` \ No newline at end of file diff --git a/src/legacy/core_plugins/vis_default_editor/public/_agg.scss b/src/plugins/vis_default_editor/public/_agg.scss similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/_agg.scss rename to src/plugins/vis_default_editor/public/_agg.scss diff --git a/src/legacy/core_plugins/vis_default_editor/public/_agg_params.scss b/src/plugins/vis_default_editor/public/_agg_params.scss similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/_agg_params.scss rename to src/plugins/vis_default_editor/public/_agg_params.scss diff --git a/src/legacy/core_plugins/vis_default_editor/public/_default.scss b/src/plugins/vis_default_editor/public/_default.scss similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/_default.scss rename to src/plugins/vis_default_editor/public/_default.scss diff --git a/src/legacy/core_plugins/vis_default_editor/public/_sidebar.scss b/src/plugins/vis_default_editor/public/_sidebar.scss similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/_sidebar.scss rename to src/plugins/vis_default_editor/public/_sidebar.scss diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/__snapshots__/agg.test.tsx.snap b/src/plugins/vis_default_editor/public/components/__snapshots__/agg.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/__snapshots__/agg.test.tsx.snap rename to src/plugins/vis_default_editor/public/components/__snapshots__/agg.test.tsx.snap diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/__snapshots__/agg_group.test.tsx.snap b/src/plugins/vis_default_editor/public/components/__snapshots__/agg_group.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/__snapshots__/agg_group.test.tsx.snap rename to src/plugins/vis_default_editor/public/components/__snapshots__/agg_group.test.tsx.snap diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg.test.tsx b/src/plugins/vis_default_editor/public/components/agg.test.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/agg.test.tsx rename to src/plugins/vis_default_editor/public/components/agg.test.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg.tsx b/src/plugins/vis_default_editor/public/components/agg.tsx similarity index 98% rename from src/legacy/core_plugins/vis_default_editor/public/components/agg.tsx rename to src/plugins/vis_default_editor/public/components/agg.tsx index 83fbf70c9099e..c7e3e609490f9 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg.tsx +++ b/src/plugins/vis_default_editor/public/components/agg.tsx @@ -28,14 +28,13 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { IAggConfig } from 'src/plugins/data/public'; +import { IAggConfig, TimeRange } from 'src/plugins/data/public'; import { DefaultEditorAggParams } from './agg_params'; import { DefaultEditorAggCommonProps } from './agg_common_props'; import { AGGS_ACTION_KEYS, AggsAction } from './agg_group_state'; import { RowsOrColumnsControl } from './controls/rows_or_columns'; import { RadiusRatioOptionControl } from './controls/radius_ratio_option'; import { getSchemaByName } from '../schemas'; -import { TimeRange } from '../../../../../plugins/data/public'; import { buildAggDescription } from './agg_params_helper'; export interface DefaultEditorAggProps extends DefaultEditorAggCommonProps { diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_add.tsx b/src/plugins/vis_default_editor/public/components/agg_add.tsx similarity index 98% rename from src/legacy/core_plugins/vis_default_editor/public/components/agg_add.tsx rename to src/plugins/vis_default_editor/public/components/agg_add.tsx index 9df4ea58e0f07..f2c2f8b4d0b94 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_add.tsx +++ b/src/plugins/vis_default_editor/public/components/agg_add.tsx @@ -29,7 +29,7 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { IAggConfig, AggGroupNames } from '../../../../../plugins/data/public'; +import { IAggConfig, AggGroupNames } from '../../../data/public'; import { Schema } from '../schemas'; interface DefaultEditorAggAddProps { diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_common_props.ts b/src/plugins/vis_default_editor/public/components/agg_common_props.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/agg_common_props.ts rename to src/plugins/vis_default_editor/public/components/agg_common_props.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_group.test.tsx b/src/plugins/vis_default_editor/public/components/agg_group.test.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/agg_group.test.tsx rename to src/plugins/vis_default_editor/public/components/agg_group.test.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_group.tsx b/src/plugins/vis_default_editor/public/components/agg_group.tsx similarity index 97% rename from src/legacy/core_plugins/vis_default_editor/public/components/agg_group.tsx rename to src/plugins/vis_default_editor/public/components/agg_group.tsx index 792595fd421f6..ecbc41f28003c 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_group.tsx +++ b/src/plugins/vis_default_editor/public/components/agg_group.tsx @@ -30,7 +30,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { AggGroupNames, search, IAggConfig } from '../../../../../plugins/data/public'; +import { AggGroupNames, search, IAggConfig, TimeRange } from '../../../data/public'; import { DefaultEditorAgg } from './agg'; import { DefaultEditorAggAdd } from './agg_add'; import { AddSchema, ReorderAggs, DefaultEditorAggCommonProps } from './agg_common_props'; @@ -42,7 +42,6 @@ import { } from './agg_group_helper'; import { aggGroupReducer, initAggsState, AGGS_ACTION_KEYS } from './agg_group_state'; import { Schema } from '../schemas'; -import { TimeRange } from '../../../../../plugins/data/public'; export interface DefaultEditorAggGroupProps extends DefaultEditorAggCommonProps { schemas: Schema[]; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_group_helper.test.ts b/src/plugins/vis_default_editor/public/components/agg_group_helper.test.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/agg_group_helper.test.ts rename to src/plugins/vis_default_editor/public/components/agg_group_helper.test.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_group_helper.tsx b/src/plugins/vis_default_editor/public/components/agg_group_helper.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/agg_group_helper.tsx rename to src/plugins/vis_default_editor/public/components/agg_group_helper.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_group_state.tsx b/src/plugins/vis_default_editor/public/components/agg_group_state.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/agg_group_state.tsx rename to src/plugins/vis_default_editor/public/components/agg_group_state.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_param.tsx b/src/plugins/vis_default_editor/public/components/agg_param.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/agg_param.tsx rename to src/plugins/vis_default_editor/public/components/agg_param.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_param_props.ts b/src/plugins/vis_default_editor/public/components/agg_param_props.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/agg_param_props.ts rename to src/plugins/vis_default_editor/public/components/agg_param_props.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_params.test.tsx b/src/plugins/vis_default_editor/public/components/agg_params.test.tsx similarity index 96% rename from src/legacy/core_plugins/vis_default_editor/public/components/agg_params.test.tsx rename to src/plugins/vis_default_editor/public/components/agg_params.test.tsx index 1c49ebf40640e..cac1b0851b92d 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_params.test.tsx +++ b/src/plugins/vis_default_editor/public/components/agg_params.test.tsx @@ -25,8 +25,8 @@ import { DefaultEditorAggParams as PureDefaultEditorAggParams, DefaultEditorAggParamsProps, } from './agg_params'; -import { KibanaContextProvider } from '../../../../../plugins/kibana_react/public'; -import { dataPluginMock } from '../../../../../plugins/data/public/mocks'; +import { KibanaContextProvider } from '../../../kibana_react/public'; +import { dataPluginMock } from '../../../data/public/mocks'; import { EditorVisState } from './sidebar/state/reducers'; const mockEditorConfig = { diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_params.tsx b/src/plugins/vis_default_editor/public/components/agg_params.tsx similarity index 98% rename from src/legacy/core_plugins/vis_default_editor/public/components/agg_params.tsx rename to src/plugins/vis_default_editor/public/components/agg_params.tsx index b1555b76500d0..3674e39b558d2 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_params.tsx +++ b/src/plugins/vis_default_editor/public/components/agg_params.tsx @@ -22,7 +22,7 @@ import { EuiForm, EuiAccordion, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import useUnmount from 'react-use/lib/useUnmount'; -import { IAggConfig, IndexPattern, AggGroupNames } from '../../../../../plugins/data/public'; +import { IAggConfig, IndexPattern, AggGroupNames } from '../../../data/public'; import { DefaultEditorAggSelect } from './agg_select'; import { DefaultEditorAggParam } from './agg_param'; @@ -40,7 +40,7 @@ import { import { DefaultEditorCommonProps } from './agg_common_props'; import { EditorParamConfig, TimeIntervalParam, FixedParam, getEditorConfig } from './utils'; import { Schema, getSchemaByName } from '../schemas'; -import { useKibana } from '../../../../../plugins/kibana_react/public'; +import { useKibana } from '../../../kibana_react/public'; import { VisDefaultEditorKibanaServices } from '../types'; const FIXED_VALUE_PROP = 'fixedValue'; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_params_helper.test.ts b/src/plugins/vis_default_editor/public/components/agg_params_helper.test.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/agg_params_helper.test.ts rename to src/plugins/vis_default_editor/public/components/agg_params_helper.test.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_params_helper.ts b/src/plugins/vis_default_editor/public/components/agg_params_helper.ts similarity index 99% rename from src/legacy/core_plugins/vis_default_editor/public/components/agg_params_helper.ts rename to src/plugins/vis_default_editor/public/components/agg_params_helper.ts index 073cb7d5ac66c..a32bd76bafa5a 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_params_helper.ts +++ b/src/plugins/vis_default_editor/public/components/agg_params_helper.ts @@ -34,7 +34,7 @@ import { AggParamEditorProps } from './agg_param_props'; import { aggParamsMap } from './agg_params_map'; import { EditorConfig } from './utils'; import { Schema, getSchemaByName } from '../schemas'; -import { search } from '../../../../../plugins/data/public'; +import { search } from '../../../data/public'; import { EditorVisState } from './sidebar/state/reducers'; interface ParamInstanceBase { diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_params_map.ts b/src/plugins/vis_default_editor/public/components/agg_params_map.ts similarity index 96% rename from src/legacy/core_plugins/vis_default_editor/public/components/agg_params_map.ts rename to src/plugins/vis_default_editor/public/components/agg_params_map.ts index 4517313b6fd6e..5af3cfc5b0928 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_params_map.ts +++ b/src/plugins/vis_default_editor/public/components/agg_params_map.ts @@ -18,12 +18,7 @@ */ import * as controls from './controls'; -import { - AggGroupNames, - BUCKET_TYPES, - METRIC_TYPES, - search, -} from '../../../../../plugins/data/public'; +import { AggGroupNames, BUCKET_TYPES, METRIC_TYPES, search } from '../../../data/public'; import { wrapWithInlineComp } from './controls/utils'; const { siblingPipelineType, parentPipelineType } = search.aggs; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_params_state.ts b/src/plugins/vis_default_editor/public/components/agg_params_state.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/agg_params_state.ts rename to src/plugins/vis_default_editor/public/components/agg_params_state.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/agg_select.tsx b/src/plugins/vis_default_editor/public/components/agg_select.tsx similarity index 98% rename from src/legacy/core_plugins/vis_default_editor/public/components/agg_select.tsx rename to src/plugins/vis_default_editor/public/components/agg_select.tsx index 7ee432946f3c8..6cb76b18e24a6 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/agg_select.tsx +++ b/src/plugins/vis_default_editor/public/components/agg_select.tsx @@ -24,7 +24,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { IAggType, IndexPattern } from 'src/plugins/data/public'; -import { useKibana } from '../../../../../plugins/kibana_react/public'; +import { useKibana } from '../../../kibana_react/public'; import { ComboBoxGroupedOptions } from '../utils'; import { AGG_TYPE_ACTION_KEYS, AggTypeAction } from './agg_params_state'; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/__snapshots__/extended_bounds.test.tsx.snap b/src/plugins/vis_default_editor/public/components/controls/__snapshots__/extended_bounds.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/__snapshots__/extended_bounds.test.tsx.snap rename to src/plugins/vis_default_editor/public/components/controls/__snapshots__/extended_bounds.test.tsx.snap diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/__snapshots__/metric_agg.test.tsx.snap b/src/plugins/vis_default_editor/public/components/controls/__snapshots__/metric_agg.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/__snapshots__/metric_agg.test.tsx.snap rename to src/plugins/vis_default_editor/public/components/controls/__snapshots__/metric_agg.test.tsx.snap diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/__snapshots__/size.test.tsx.snap b/src/plugins/vis_default_editor/public/components/controls/__snapshots__/size.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/__snapshots__/size.test.tsx.snap rename to src/plugins/vis_default_editor/public/components/controls/__snapshots__/size.test.tsx.snap diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/__snapshots__/top_aggregate.test.tsx.snap b/src/plugins/vis_default_editor/public/components/controls/__snapshots__/top_aggregate.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/__snapshots__/top_aggregate.test.tsx.snap rename to src/plugins/vis_default_editor/public/components/controls/__snapshots__/top_aggregate.test.tsx.snap diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/agg_control_props.tsx b/src/plugins/vis_default_editor/public/components/controls/agg_control_props.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/agg_control_props.tsx rename to src/plugins/vis_default_editor/public/components/controls/agg_control_props.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/agg_utils.test.tsx b/src/plugins/vis_default_editor/public/components/controls/agg_utils.test.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/agg_utils.test.tsx rename to src/plugins/vis_default_editor/public/components/controls/agg_utils.test.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/auto_precision.tsx b/src/plugins/vis_default_editor/public/components/controls/auto_precision.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/auto_precision.tsx rename to src/plugins/vis_default_editor/public/components/controls/auto_precision.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/components/from_to_list.tsx b/src/plugins/vis_default_editor/public/components/controls/components/from_to_list.tsx similarity index 68% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/components/from_to_list.tsx rename to src/plugins/vis_default_editor/public/components/controls/components/from_to_list.tsx index e52b2c85b63fa..b874459a8e7d3 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/components/from_to_list.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/components/from_to_list.tsx @@ -17,11 +17,11 @@ * under the License. */ -import React from 'react'; +import React, { useCallback } from 'react'; import { EuiFieldText, EuiFlexItem, EuiIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { Ipv4Address } from '../../../../../../../plugins/kibana_utils/public'; +import { Ipv4Address } from '../../../../../kibana_utils/public'; import { InputList, InputListConfig, InputModel, InputObject, InputItem } from './input_list'; const EMPTY_STRING = ''; @@ -44,38 +44,42 @@ interface FromToListProps { setValidity(isValid: boolean): void; } -function FromToList({ showValidation, onBlur, ...rest }: FromToListProps) { - const fromToListConfig: InputListConfig = { - defaultValue: { - from: { value: '0.0.0.0', model: '0.0.0.0', isInvalid: false }, - to: { value: '255.255.255.255', model: '255.255.255.255', isInvalid: false }, +const defaultConfig = { + defaultValue: { + from: { value: '0.0.0.0', model: '0.0.0.0', isInvalid: false }, + to: { value: '255.255.255.255', model: '255.255.255.255', isInvalid: false }, + }, + validateClass: Ipv4Address, + getModelValue: (item: FromToObject = {}) => ({ + from: { + value: item.from || EMPTY_STRING, + model: item.from || EMPTY_STRING, + isInvalid: false, }, - validateClass: Ipv4Address, - getModelValue: (item: FromToObject = {}) => ({ - from: { - value: item.from || EMPTY_STRING, - model: item.from || EMPTY_STRING, - isInvalid: false, - }, - to: { value: item.to || EMPTY_STRING, model: item.to || EMPTY_STRING, isInvalid: false }, + to: { value: item.to || EMPTY_STRING, model: item.to || EMPTY_STRING, isInvalid: false }, + }), + getRemoveBtnAriaLabel: (item: FromToModel) => + i18n.translate('visDefaultEditor.controls.ipRanges.removeRangeAriaLabel', { + defaultMessage: 'Remove the range of {from} to {to}', + values: { from: item.from.value || '*', to: item.to.value || '*' }, }), - getRemoveBtnAriaLabel: (item: FromToModel) => - i18n.translate('visDefaultEditor.controls.ipRanges.removeRangeAriaLabel', { - defaultMessage: 'Remove the range of {from} to {to}', - values: { from: item.from.value || '*', to: item.to.value || '*' }, - }), - onChangeFn: ({ from, to }: FromToModel) => { - const result: FromToObject = {}; - if (from.model) { - result.from = from.model; - } - if (to.model) { - result.to = to.model; - } - return result; - }, - hasInvalidValuesFn: ({ from, to }: FromToModel) => from.isInvalid || to.isInvalid, - renderInputRow: (item: FromToModel, index, onChangeValue) => ( + onChangeFn: ({ from, to }: FromToModel) => { + const result: FromToObject = {}; + if (from.model) { + result.from = from.model; + } + if (to.model) { + result.to = to.model; + } + return result; + }, + hasInvalidValuesFn: ({ from, to }: FromToModel) => from.isInvalid || to.isInvalid, + modelNames: ['from', 'to'], +}; + +function FromToList({ showValidation, onBlur, ...rest }: FromToListProps) { + const renderInputRow = useCallback( + (item: FromToModel, index, onChangeValue) => ( <> ), - modelNames: ['from', 'to'], + [onBlur, showValidation] + ); + const fromToListConfig: InputListConfig = { + ...defaultConfig, + renderInputRow, }; return ; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/components/input_list.tsx b/src/plugins/vis_default_editor/public/components/controls/components/input_list.tsx similarity index 75% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/components/input_list.tsx rename to src/plugins/vis_default_editor/public/components/controls/components/input_list.tsx index cc80d0073c904..639b69cd3d33c 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/components/input_list.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/components/input_list.tsx @@ -17,7 +17,7 @@ * under the License. */ -import React, { useState, useEffect, Fragment } from 'react'; +import React, { useState, useEffect, Fragment, useCallback } from 'react'; import { isEmpty, isEqual, mapValues, omit, pick } from 'lodash'; import { EuiButtonIcon, @@ -70,7 +70,10 @@ interface InputListProps { } const generateId = htmlIdGenerator(); -const validateValue = (inputValue: string | undefined, config: InputListConfig) => { +const validateValue = ( + inputValue: string | undefined, + InputObject: InputListConfig['validateClass'] +) => { const result = { model: inputValue || '', isInvalid: false, @@ -80,7 +83,6 @@ const validateValue = (inputValue: string | undefined, config: InputListConfig) return result; } try { - const InputObject = config.validateClass; result.model = new InputObject(inputValue).toString(); result.isInvalid = false; return result; @@ -91,47 +93,60 @@ const validateValue = (inputValue: string | undefined, config: InputListConfig) }; function InputList({ config, list, onChange, setValidity }: InputListProps) { + const { defaultValue, getModelValue, modelNames, onChangeFn, validateClass } = config; const [models, setModels] = useState(() => list.map( item => ({ id: generateId(), - ...config.getModelValue(item), + ...getModelValue(item), } as InputModel) ) ); const hasInvalidValues = models.some(config.hasInvalidValuesFn); - const updateValues = (modelList: InputModel[]) => { - setModels(modelList); - onChange(modelList.map(config.onChangeFn)); - }; - const onChangeValue = (index: number, value: string, modelName: string) => { - const { model, isInvalid } = validateValue(value, config); - updateValues( - models.map((range, arrayIndex) => - arrayIndex === index - ? { - ...range, - [modelName]: { - value, - model, - isInvalid, - }, - } - : range - ) - ); - }; - const onDelete = (id: string) => updateValues(models.filter(model => model.id !== id)); - const onAdd = () => - updateValues([ - ...models, - { - id: generateId(), - ...config.getModelValue(), - } as InputModel, - ]); + const updateValues = useCallback( + (modelList: InputModel[]) => { + setModels(modelList); + onChange(modelList.map(onChangeFn)); + }, + [onChangeFn, onChange] + ); + const onChangeValue = useCallback( + (index: number, value: string, modelName: string) => { + const { model, isInvalid } = validateValue(value, validateClass); + updateValues( + models.map((range, arrayIndex) => + arrayIndex === index + ? { + ...range, + [modelName]: { + value, + model, + isInvalid, + }, + } + : range + ) + ); + }, + [models, updateValues, validateClass] + ); + const onDelete = useCallback( + (id: string) => updateValues(models.filter(model => model.id !== id)), + [models, updateValues] + ); + const onAdd = useCallback( + () => + updateValues([ + ...models, + { + id: generateId(), + ...getModelValue(), + } as InputModel, + ]), + [getModelValue, models, updateValues] + ); useEffect(() => { // resposible for setting up an initial value when there is no default value @@ -139,15 +154,15 @@ function InputList({ config, list, onChange, setValidity }: InputListProps) { updateValues([ { id: generateId(), - ...config.defaultValue, + ...defaultValue, } as InputModel, ]); } - }, []); + }, [defaultValue, list.length, updateValues]); useEffect(() => { setValidity(!hasInvalidValues); - }, [hasInvalidValues]); + }, [hasInvalidValues, setValidity]); useEffect(() => { // responsible for discarding changes @@ -155,7 +170,7 @@ function InputList({ config, list, onChange, setValidity }: InputListProps) { list.length !== models.length || list.some((item, index) => { // make model to be the same shape as stored value - const model: InputObject = mapValues(pick(models[index], config.modelNames), 'model'); + const model: InputObject = mapValues(pick(models[index], modelNames), 'model'); // we need to skip empty values since they are not stored in saved object return !isEqual(item, omit(model, isEmpty)); @@ -166,12 +181,12 @@ function InputList({ config, list, onChange, setValidity }: InputListProps) { item => ({ id: generateId(), - ...config.getModelValue(item), + ...getModelValue(item), } as InputModel) ) ); } - }, [list]); + }, [getModelValue, list, modelNames, models]); return ( <> diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/components/mask_list.tsx b/src/plugins/vis_default_editor/public/components/controls/components/mask_list.tsx similarity index 62% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/components/mask_list.tsx rename to src/plugins/vis_default_editor/public/components/controls/components/mask_list.tsx index f6edecbbcbd70..560213fc08ff0 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/components/mask_list.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/components/mask_list.tsx @@ -17,12 +17,12 @@ * under the License. */ -import React from 'react'; +import React, { useCallback } from 'react'; import { EuiFieldText, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { InputList, InputListConfig, InputObject, InputModel, InputItem } from './input_list'; -import { search } from '../../../../../../../plugins/data/public'; +import { search } from '../../../../../data/public'; const EMPTY_STRING = ''; @@ -42,36 +42,40 @@ interface MaskListProps { setValidity(isValid: boolean): void; } -function MaskList({ showValidation, onBlur, ...rest }: MaskListProps) { - const maskListConfig: InputListConfig = { - defaultValue: { - mask: { model: '0.0.0.0/1', value: '0.0.0.0/1', isInvalid: false }, +const defaultConfig = { + defaultValue: { + mask: { model: '0.0.0.0/1', value: '0.0.0.0/1', isInvalid: false }, + }, + validateClass: search.aggs.CidrMask, + getModelValue: (item: MaskObject = {}) => ({ + mask: { + model: item.mask || EMPTY_STRING, + value: item.mask || EMPTY_STRING, + isInvalid: false, }, - validateClass: search.aggs.CidrMask, - getModelValue: (item: MaskObject = {}) => ({ - mask: { - model: item.mask || EMPTY_STRING, - value: item.mask || EMPTY_STRING, - isInvalid: false, - }, - }), - getRemoveBtnAriaLabel: (item: MaskModel) => - item.mask.value - ? i18n.translate('visDefaultEditor.controls.ipRanges.removeCidrMaskButtonAriaLabel', { - defaultMessage: 'Remove the CIDR mask value of {mask}', - values: { mask: item.mask.value }, - }) - : i18n.translate('visDefaultEditor.controls.ipRanges.removeEmptyCidrMaskButtonAriaLabel', { - defaultMessage: 'Remove the CIDR mask default value', - }), - onChangeFn: ({ mask }: MaskModel) => { - if (mask.model) { - return { mask: mask.model }; - } - return {}; - }, - hasInvalidValuesFn: ({ mask }) => mask.isInvalid, - renderInputRow: ({ mask }: MaskModel, index, onChangeValue) => ( + }), + getRemoveBtnAriaLabel: (item: MaskModel) => + item.mask.value + ? i18n.translate('visDefaultEditor.controls.ipRanges.removeCidrMaskButtonAriaLabel', { + defaultMessage: 'Remove the CIDR mask value of {mask}', + values: { mask: item.mask.value }, + }) + : i18n.translate('visDefaultEditor.controls.ipRanges.removeEmptyCidrMaskButtonAriaLabel', { + defaultMessage: 'Remove the CIDR mask default value', + }), + onChangeFn: ({ mask }: MaskModel) => { + if (mask.model) { + return { mask: mask.model }; + } + return {}; + }, + hasInvalidValuesFn: ({ mask }: MaskModel) => mask.isInvalid, + modelNames: 'mask', +}; + +function MaskList({ showValidation, onBlur, ...rest }: MaskListProps) { + const renderInputRow = useCallback( + ({ mask }: MaskModel, index, onChangeValue) => ( ), - modelNames: 'mask', + [onBlur, showValidation] + ); + const maskListConfig: InputListConfig = { + ...defaultConfig, + renderInputRow, }; return ; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/__snapshots__/number_list.test.tsx.snap b/src/plugins/vis_default_editor/public/components/controls/components/number_list/__snapshots__/number_list.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/__snapshots__/number_list.test.tsx.snap rename to src/plugins/vis_default_editor/public/components/controls/components/number_list/__snapshots__/number_list.test.tsx.snap diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/__snapshots__/number_row.test.tsx.snap b/src/plugins/vis_default_editor/public/components/controls/components/number_list/__snapshots__/number_row.test.tsx.snap similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/__snapshots__/number_row.test.tsx.snap rename to src/plugins/vis_default_editor/public/components/controls/components/number_list/__snapshots__/number_row.test.tsx.snap diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/index.ts b/src/plugins/vis_default_editor/public/components/controls/components/number_list/index.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/index.ts rename to src/plugins/vis_default_editor/public/components/controls/components/number_list/index.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/number_list.test.tsx b/src/plugins/vis_default_editor/public/components/controls/components/number_list/number_list.test.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/number_list.test.tsx rename to src/plugins/vis_default_editor/public/components/controls/components/number_list/number_list.test.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/number_list.tsx b/src/plugins/vis_default_editor/public/components/controls/components/number_list/number_list.tsx similarity index 98% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/number_list.tsx rename to src/plugins/vis_default_editor/public/components/controls/components/number_list/number_list.tsx index a43c66c2e08cc..b4683e47979ce 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/number_list.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/components/number_list/number_list.tsx @@ -79,7 +79,7 @@ function NumberList({ if (!numberArray.length) { onChange([models[0].value as number]); } - }, []); + }, [models, numberArray.length, onChange]); const isValid = !hasInvalidValues(models); useValidation(setValidity, isValid); @@ -109,7 +109,7 @@ function NumberList({ }) ); }, - [numberRange, models, onUpdate] + [models, onUpdate] ); // Add an item to the end of the list diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/number_row.test.tsx b/src/plugins/vis_default_editor/public/components/controls/components/number_list/number_row.test.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/number_row.test.tsx rename to src/plugins/vis_default_editor/public/components/controls/components/number_list/number_row.test.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/number_row.tsx b/src/plugins/vis_default_editor/public/components/controls/components/number_list/number_row.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/number_row.tsx rename to src/plugins/vis_default_editor/public/components/controls/components/number_list/number_row.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/range.test.ts b/src/plugins/vis_default_editor/public/components/controls/components/number_list/range.test.ts similarity index 98% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/range.test.ts rename to src/plugins/vis_default_editor/public/components/controls/components/number_list/range.test.ts index e9090e5b38ef7..a19034d3c8e92 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/range.test.ts +++ b/src/plugins/vis_default_editor/public/components/controls/components/number_list/range.test.ts @@ -108,7 +108,6 @@ describe('Range parsing utility', () => { }; forOwn(tests, (spec, str: any) => { - // eslint-disable-next-line jest/valid-describe describe(str, () => { const range = parseRange(str); diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/range.ts b/src/plugins/vis_default_editor/public/components/controls/components/number_list/range.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/range.ts rename to src/plugins/vis_default_editor/public/components/controls/components/number_list/range.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/utils.test.ts b/src/plugins/vis_default_editor/public/components/controls/components/number_list/utils.test.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/utils.test.ts rename to src/plugins/vis_default_editor/public/components/controls/components/number_list/utils.test.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/utils.ts b/src/plugins/vis_default_editor/public/components/controls/components/number_list/utils.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/components/number_list/utils.ts rename to src/plugins/vis_default_editor/public/components/controls/components/number_list/utils.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/date_ranges.test.tsx b/src/plugins/vis_default_editor/public/components/controls/date_ranges.test.tsx similarity index 95% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/date_ranges.test.tsx rename to src/plugins/vis_default_editor/public/components/controls/date_ranges.test.tsx index 6b1a4dca7b84f..b844fdfb82256 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/date_ranges.test.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/date_ranges.test.tsx @@ -20,8 +20,8 @@ import React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { DateRangesParamEditor } from './date_ranges'; -import { KibanaContextProvider } from '../../../../../../plugins/kibana_react/public'; -import { docLinksServiceMock } from '../../../../../../core/public/mocks'; +import { KibanaContextProvider } from '../../../../kibana_react/public'; +import { docLinksServiceMock } from '../../../../../core/public/mocks'; describe('DateRangesParamEditor component', () => { let setValue: jest.Mock; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/date_ranges.tsx b/src/plugins/vis_default_editor/public/components/controls/date_ranges.tsx similarity index 90% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/date_ranges.tsx rename to src/plugins/vis_default_editor/public/components/controls/date_ranges.tsx index 15e864bfd026d..57f4c7d04019b 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/date_ranges.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/date_ranges.tsx @@ -17,7 +17,7 @@ * under the License. */ -import React, { Fragment, useState, useEffect } from 'react'; +import React, { Fragment, useState, useEffect, useCallback } from 'react'; import { htmlIdGenerator, EuiButtonIcon, @@ -36,8 +36,9 @@ import dateMath from '@elastic/datemath'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { isEqual, omit } from 'lodash'; +import { useMount } from 'react-use'; -import { useKibana } from '../../../../../../plugins/kibana_react/public'; +import { useKibana } from '../../../../kibana_react/public'; import { AggParamEditorProps } from '../agg_param_props'; const FROM_PLACEHOLDER = '\u2212\u221E'; @@ -72,12 +73,26 @@ function DateRangesParamEditor({ ({ from, to }) => (!from && !to) || !validateDateMath(from) || !validateDateMath(to) ); - // set up an initial range when there is no default range - useEffect(() => { + const updateRanges = useCallback( + (rangeValues: DateRangeValuesModel[]) => { + // do not set internal id parameter into saved object + setValue(rangeValues.map(range => omit(range, 'id'))); + setRanges(rangeValues); + }, + [setValue] + ); + + const onAddRange = useCallback(() => updateRanges([...ranges, { id: generateId() }]), [ + ranges, + updateRanges, + ]); + + useMount(() => { + // set up an initial range when there is no default range if (!value.length) { onAddRange(); } - }, []); + }); useEffect(() => { // responsible for discarding changes @@ -87,18 +102,12 @@ function DateRangesParamEditor({ ) { setRanges(value.map(range => ({ ...range, id: generateId() }))); } - }, [value]); + }, [ranges, value]); useEffect(() => { setValidity(!hasInvalidRange); - }, [hasInvalidRange]); - - const updateRanges = (rangeValues: DateRangeValuesModel[]) => { - // do not set internal id parameter into saved object - setValue(rangeValues.map(range => omit(range, 'id'))); - setRanges(rangeValues); - }; - const onAddRange = () => updateRanges([...ranges, { id: generateId() }]); + }, [hasInvalidRange, setValidity]); + const onRemoveRange = (id: string) => updateRanges(ranges.filter(range => range.id !== id)); const onChangeRange = (id: string, key: string, newValue: string) => updateRanges( diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/drop_partials.tsx b/src/plugins/vis_default_editor/public/components/controls/drop_partials.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/drop_partials.tsx rename to src/plugins/vis_default_editor/public/components/controls/drop_partials.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/extended_bounds.test.tsx b/src/plugins/vis_default_editor/public/components/controls/extended_bounds.test.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/extended_bounds.test.tsx rename to src/plugins/vis_default_editor/public/components/controls/extended_bounds.test.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/extended_bounds.tsx b/src/plugins/vis_default_editor/public/components/controls/extended_bounds.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/extended_bounds.tsx rename to src/plugins/vis_default_editor/public/components/controls/extended_bounds.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/field.test.tsx b/src/plugins/vis_default_editor/public/components/controls/field.test.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/field.test.tsx rename to src/plugins/vis_default_editor/public/components/controls/field.test.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/field.tsx b/src/plugins/vis_default_editor/public/components/controls/field.tsx similarity index 97% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/field.tsx rename to src/plugins/vis_default_editor/public/components/controls/field.tsx index fc1cbb51b70a7..42086004a12dc 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/field.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/field.tsx @@ -18,7 +18,8 @@ */ import { get } from 'lodash'; -import React, { useEffect, useState, useCallback } from 'react'; +import React, { useState, useCallback } from 'react'; +import { useMount } from 'react-use'; import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -84,8 +85,7 @@ function FieldParamEditor({ const showErrorMessage = (showValidation || !indexedFields.length) && !isValid; useValidation(setValidity, isValid); - - useEffect(() => { + useMount(() => { // set field if only one available if (indexedFields.length !== 1) { return; @@ -98,7 +98,7 @@ function FieldParamEditor({ } else if (indexedField.options.length === 1) { setValue(indexedField.options[0].target); } - }, []); + }); const onSearchChange = useCallback(searchValue => setIsDirty(Boolean(searchValue)), []); diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/filter.tsx b/src/plugins/vis_default_editor/public/components/controls/filter.tsx similarity index 99% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/filter.tsx rename to src/plugins/vis_default_editor/public/components/controls/filter.tsx index e2e7c2895093e..16aaff47f49c3 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/filter.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/filter.tsx @@ -21,7 +21,7 @@ import React, { useState } from 'react'; import { EuiForm, EuiButtonIcon, EuiFieldText, EuiFormRow, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { IAggConfig, Query, QueryStringInput } from '../../../../../../plugins/data/public'; +import { IAggConfig, Query, QueryStringInput } from '../../../../data/public'; interface FilterRowProps { id: string; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/filters.tsx b/src/plugins/vis_default_editor/public/components/controls/filters.tsx similarity index 96% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/filters.tsx rename to src/plugins/vis_default_editor/public/components/controls/filters.tsx index be4c62ab08aa2..d4e698f655c5e 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/filters.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/filters.tsx @@ -21,9 +21,10 @@ import React, { useState, useEffect } from 'react'; import { omit, isEqual } from 'lodash'; import { htmlIdGenerator, EuiButton, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import { useMount } from 'react-use'; import { Query } from 'src/plugins/data/public'; -import { useKibana } from '../../../../../../plugins/kibana_react/public'; +import { useKibana } from '../../../../kibana_react/public'; import { FilterRow } from './filter'; import { AggParamEditorProps } from '../agg_param_props'; @@ -40,10 +41,10 @@ function FiltersParamEditor({ agg, value = [], setValue }: AggParamEditorProps ({ ...filter, id: generateId() })) ); - useEffect(() => { + useMount(() => { // set parsed values into model after initialization setValue(filters.map(filter => omit({ ...filter, input: filter.input }, 'id'))); - }, []); + }); useEffect(() => { // responsible for discarding changes @@ -53,7 +54,7 @@ function FiltersParamEditor({ agg, value = [], setValue }: AggParamEditorProps ({ ...filter, id: generateId() }))); } - }, [value]); + }, [filters, value]); const updateFilters = (updatedFilters: FilterValue[]) => { // do not set internal id parameter into saved object diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/has_extended_bounds.tsx b/src/plugins/vis_default_editor/public/components/controls/has_extended_bounds.tsx similarity index 73% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/has_extended_bounds.tsx rename to src/plugins/vis_default_editor/public/components/controls/has_extended_bounds.tsx index 90b7cb03b7a5b..a316a087c8bcb 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/has_extended_bounds.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/has_extended_bounds.tsx @@ -17,22 +17,32 @@ * under the License. */ -import React, { useEffect } from 'react'; +import React, { useEffect, useRef } from 'react'; import { i18n } from '@kbn/i18n'; -import { search } from '../../../../../../plugins/data/public'; +import { search } from '../../../../data/public'; import { SwitchParamEditor } from './switch'; import { AggParamEditorProps } from '../agg_param_props'; const { isType } = search.aggs; function HasExtendedBoundsParamEditor(props: AggParamEditorProps) { + const { agg, setValue, value } = props; + const minDocCount = useRef(agg.params.min_doc_count); + useEffect(() => { - props.setValue(props.value && props.agg.params.min_doc_count); - }, [props.agg.params.min_doc_count]); + if (minDocCount.current !== agg.params.min_doc_count) { + // The "Extend bounds" param is only enabled when "Show empty buckets" is turned on. + // So if "Show empty buckets" is changed, "Extend bounds" should reflect changes + minDocCount.current = agg.params.min_doc_count; + + setValue(value && agg.params.min_doc_count); + } + }, [agg.params.min_doc_count, setValue, value]); return ( ) { !props.agg.params.min_doc_count || !(isType('number')(props.agg) || isType('date')(props.agg)) } - {...props} /> ); } diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/index.ts b/src/plugins/vis_default_editor/public/components/controls/index.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/index.ts rename to src/plugins/vis_default_editor/public/components/controls/index.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/ip_range_type.tsx b/src/plugins/vis_default_editor/public/components/controls/ip_range_type.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/ip_range_type.tsx rename to src/plugins/vis_default_editor/public/components/controls/ip_range_type.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/ip_ranges.tsx b/src/plugins/vis_default_editor/public/components/controls/ip_ranges.tsx similarity index 79% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/ip_ranges.tsx rename to src/plugins/vis_default_editor/public/components/controls/ip_ranges.tsx index c4b90649aaaae..5ffa8088a9546 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/ip_ranges.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/ip_ranges.tsx @@ -17,7 +17,7 @@ * under the License. */ -import React from 'react'; +import React, { useCallback } from 'react'; import { EuiFormRow } from '@elastic/eui'; import { FromToList, FromToObject } from './components/from_to_list'; @@ -38,12 +38,22 @@ function IpRangesParamEditor({ setValidity, showValidation, }: AggParamEditorProps) { - const handleChange = (modelName: IpRangeTypes, items: Array) => { - setValue({ - ...value, - [modelName]: items, - }); - }; + const handleMaskListChange = useCallback( + (items: MaskObject[]) => + setValue({ + ...value, + [IpRangeTypes.MASK]: items, + }), + [setValue, value] + ); + const handleFromToListChange = useCallback( + (items: FromToObject[]) => + setValue({ + ...value, + [IpRangeTypes.FROM_TO]: items, + }), + [setValue, value] + ); return ( @@ -52,7 +62,7 @@ function IpRangesParamEditor({ list={value.mask} showValidation={showValidation} onBlur={setTouched} - onChange={items => handleChange(IpRangeTypes.MASK, items)} + onChange={handleMaskListChange} setValidity={setValidity} /> ) : ( @@ -60,7 +70,7 @@ function IpRangesParamEditor({ list={value.fromTo} showValidation={showValidation} onBlur={setTouched} - onChange={items => handleChange(IpRangeTypes.FROM_TO, items)} + onChange={handleFromToListChange} setValidity={setValidity} /> )} diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/is_filtered_by_collar.tsx b/src/plugins/vis_default_editor/public/components/controls/is_filtered_by_collar.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/is_filtered_by_collar.tsx rename to src/plugins/vis_default_editor/public/components/controls/is_filtered_by_collar.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/metric_agg.test.tsx b/src/plugins/vis_default_editor/public/components/controls/metric_agg.test.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/metric_agg.test.tsx rename to src/plugins/vis_default_editor/public/components/controls/metric_agg.test.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/metric_agg.tsx b/src/plugins/vis_default_editor/public/components/controls/metric_agg.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/metric_agg.tsx rename to src/plugins/vis_default_editor/public/components/controls/metric_agg.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/min_doc_count.tsx b/src/plugins/vis_default_editor/public/components/controls/min_doc_count.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/min_doc_count.tsx rename to src/plugins/vis_default_editor/public/components/controls/min_doc_count.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/missing_bucket.tsx b/src/plugins/vis_default_editor/public/components/controls/missing_bucket.tsx similarity index 93% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/missing_bucket.tsx rename to src/plugins/vis_default_editor/public/components/controls/missing_bucket.tsx index 7010f0d53e569..7d4d2230bd766 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/missing_bucket.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/missing_bucket.tsx @@ -21,20 +21,22 @@ import React, { useEffect } from 'react'; import { i18n } from '@kbn/i18n'; import { SwitchParamEditor } from './switch'; -import { search } from '../../../../../../plugins/data/public'; +import { search } from '../../../../data/public'; import { AggParamEditorProps } from '../agg_param_props'; function MissingBucketParamEditor(props: AggParamEditorProps) { const fieldTypeIsNotString = !search.aggs.isStringType(props.agg); + const { setValue } = props; useEffect(() => { if (fieldTypeIsNotString) { - props.setValue(false); + setValue(false); } - }, [fieldTypeIsNotString]); + }, [fieldTypeIsNotString, setValue]); return ( ) { } )} disabled={fieldTypeIsNotString} - {...props} /> ); } diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/number_interval.tsx b/src/plugins/vis_default_editor/public/components/controls/number_interval.tsx similarity index 99% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/number_interval.tsx rename to src/plugins/vis_default_editor/public/components/controls/number_interval.tsx index 6ab5ee2d260a1..02bf680734526 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/number_interval.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/number_interval.tsx @@ -61,7 +61,7 @@ function NumberIntervalParamEditor({ useEffect(() => { setValidity(isValid); - }, [isValid]); + }, [isValid, setValidity]); const onChange = useCallback( ({ target }: React.ChangeEvent) => diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/order.tsx b/src/plugins/vis_default_editor/public/components/controls/order.tsx similarity index 98% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/order.tsx rename to src/plugins/vis_default_editor/public/components/controls/order.tsx index 8f63662d928c1..e609bf9adf790 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/order.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/order.tsx @@ -39,7 +39,7 @@ function OrderParamEditor({ useEffect(() => { setValidity(isValid); - }, [isValid]); + }, [isValid, setValidity]); // @ts-ignore return ( diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/order_agg.test.tsx b/src/plugins/vis_default_editor/public/components/controls/order_agg.test.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/order_agg.test.tsx rename to src/plugins/vis_default_editor/public/components/controls/order_agg.test.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/order_agg.tsx b/src/plugins/vis_default_editor/public/components/controls/order_agg.tsx similarity index 97% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/order_agg.tsx rename to src/plugins/vis_default_editor/public/components/controls/order_agg.tsx index 41672bc192fab..c5a35cbbd7ab1 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/order_agg.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/order_agg.tsx @@ -20,7 +20,7 @@ import React, { useEffect } from 'react'; import { EuiSpacer } from '@elastic/eui'; -import { AggParamType, IAggConfig, AggGroupNames } from '../../../../../../plugins/data/public'; +import { AggParamType, IAggConfig, AggGroupNames } from '../../../../data/public'; import { useSubAggParamsHandlers } from './utils'; import { AggParamEditorProps } from '../agg_param_props'; import { DefaultEditorAggParams } from '../agg_params'; @@ -47,7 +47,7 @@ function OrderAggParamEditor({ if (orderBy !== 'custom' && value) { setValue(undefined); } - }, [orderBy]); + }, [agg, aggParam, orderBy, setValue, value]); const { onAggTypeChange, setAggParamValue } = useSubAggParamsHandlers( agg, diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/order_by.tsx b/src/plugins/vis_default_editor/public/components/controls/order_by.tsx similarity index 94% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/order_by.tsx rename to src/plugins/vis_default_editor/public/components/controls/order_by.tsx index 9f1aaa54a8ca3..47b12f4340d42 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/order_by.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/order_by.tsx @@ -17,9 +17,10 @@ * under the License. */ -import React, { useEffect } from 'react'; +import React from 'react'; import { EuiFormRow, EuiSelect } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { useMount } from 'react-use'; import { isCompatibleAggregation, @@ -28,7 +29,7 @@ import { useValidation, } from './utils'; import { AggParamEditorProps } from '../agg_param_props'; -import { search } from '../../../../../../plugins/data/public'; +import { search } from '../../../../data/public'; const { termsAggFilter } = search.aggs; const DEFAULT_VALUE = '_key'; @@ -58,8 +59,7 @@ function OrderByParamEditor({ const isValid = !!value; useValidation(setValidity, isValid); - - useEffect(() => { + useMount(() => { // setup the initial value of orderBy if (!value) { let respAgg = { id: DEFAULT_VALUE }; @@ -70,7 +70,7 @@ function OrderByParamEditor({ setValue(respAgg.id); } - }, []); + }); useFallbackMetric(setValue, termsAggFilter, metricAggs, value, DEFAULT_VALUE); diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/other_bucket.tsx b/src/plugins/vis_default_editor/public/components/controls/other_bucket.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/other_bucket.tsx rename to src/plugins/vis_default_editor/public/components/controls/other_bucket.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/percentile_ranks.tsx b/src/plugins/vis_default_editor/public/components/controls/percentile_ranks.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/percentile_ranks.tsx rename to src/plugins/vis_default_editor/public/components/controls/percentile_ranks.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/percentiles.test.tsx b/src/plugins/vis_default_editor/public/components/controls/percentiles.test.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/percentiles.test.tsx rename to src/plugins/vis_default_editor/public/components/controls/percentiles.test.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/percentiles.tsx b/src/plugins/vis_default_editor/public/components/controls/percentiles.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/percentiles.tsx rename to src/plugins/vis_default_editor/public/components/controls/percentiles.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/precision.tsx b/src/plugins/vis_default_editor/public/components/controls/precision.tsx similarity index 95% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/precision.tsx rename to src/plugins/vis_default_editor/public/components/controls/precision.tsx index df71e72ee6c61..ad2011513b171 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/precision.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/precision.tsx @@ -22,7 +22,7 @@ import React from 'react'; import { EuiRange, EuiFormRow } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { useKibana } from '../../../../../../plugins/kibana_react/public'; +import { useKibana } from '../../../../kibana_react/public'; import { AggParamEditorProps } from '../agg_param_props'; function PrecisionParamEditor({ agg, value, setValue }: AggParamEditorProps) { diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/radius_ratio_option.tsx b/src/plugins/vis_default_editor/public/components/controls/radius_ratio_option.tsx similarity index 95% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/radius_ratio_option.tsx rename to src/plugins/vis_default_editor/public/components/controls/radius_ratio_option.tsx index c64b079e4f802..86c4431b6d5ed 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/radius_ratio_option.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/radius_ratio_option.tsx @@ -17,10 +17,11 @@ * under the License. */ -import React, { useEffect, useCallback } from 'react'; +import React, { useCallback } from 'react'; import { EuiFormRow, EuiIconTip, EuiRange, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import { useMount } from 'react-use'; import { AggControlProps } from './agg_control_props'; @@ -44,11 +45,11 @@ function RadiusRatioOptionControl({ editorStateParams, setStateParamValue }: Agg ); - useEffect(() => { + useMount(() => { if (!editorStateParams.radiusRatio) { setStateParamValue(PARAM_NAME, DEFAULT_VALUE); } - }, []); + }); const onChange = useCallback( (e: React.ChangeEvent | React.MouseEvent) => diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/range_control.tsx b/src/plugins/vis_default_editor/public/components/controls/range_control.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/range_control.tsx rename to src/plugins/vis_default_editor/public/components/controls/range_control.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/ranges.tsx b/src/plugins/vis_default_editor/public/components/controls/ranges.tsx similarity index 91% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/ranges.tsx rename to src/plugins/vis_default_editor/public/components/controls/ranges.tsx index 27de9dfe68ee0..5c6438b400408 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/ranges.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/ranges.tsx @@ -17,7 +17,7 @@ * under the License. */ -import React, { Fragment, useState, useEffect } from 'react'; +import React, { Fragment, useCallback, useState, useEffect } from 'react'; import { htmlIdGenerator, EuiButtonIcon, @@ -76,13 +76,44 @@ function RangesParamEditor({ validateRange, }: RangesParamEditorProps) { const [ranges, setRanges] = useState(() => value.map(range => ({ ...range, id: generateId() }))); + const updateRanges = useCallback( + (rangeValues: RangeValuesModel[]) => { + // do not set internal id parameter into saved object + setValue(rangeValues.map(range => omit(range, 'id'))); + setRanges(rangeValues); + + if (setTouched) { + setTouched(true); + } + }, + [setTouched, setValue] + ); + const onAddRange = useCallback( + () => + addRangeValues + ? updateRanges([...ranges, { ...addRangeValues(), id: generateId() }]) + : updateRanges([...ranges, { id: generateId() }]), + [addRangeValues, ranges, updateRanges] + ); + const onRemoveRange = (id: string) => updateRanges(ranges.filter(range => range.id !== id)); + const onChangeRange = (id: string, key: string, newValue: string) => + updateRanges( + ranges.map(range => + range.id === id + ? { + ...range, + [key]: newValue === '' ? undefined : parseFloat(newValue), + } + : range + ) + ); // set up an initial range when there is no default range useEffect(() => { if (!value.length) { onAddRange(); } - }, []); + }, [onAddRange, value.length]); useEffect(() => { // responsible for discarding changes @@ -92,33 +123,7 @@ function RangesParamEditor({ ) { setRanges(value.map(range => ({ ...range, id: generateId() }))); } - }, [value]); - - const updateRanges = (rangeValues: RangeValuesModel[]) => { - // do not set internal id parameter into saved object - setValue(rangeValues.map(range => omit(range, 'id'))); - setRanges(rangeValues); - - if (setTouched) { - setTouched(true); - } - }; - const onAddRange = () => - addRangeValues - ? updateRanges([...ranges, { ...addRangeValues(), id: generateId() }]) - : updateRanges([...ranges, { id: generateId() }]); - const onRemoveRange = (id: string) => updateRanges(ranges.filter(range => range.id !== id)); - const onChangeRange = (id: string, key: string, newValue: string) => - updateRanges( - ranges.map(range => - range.id === id - ? { - ...range, - [key]: newValue === '' ? undefined : parseFloat(newValue), - } - : range - ) - ); + }, [ranges, value]); const hasInvalidRange = validateRange && diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/raw_json.tsx b/src/plugins/vis_default_editor/public/components/controls/raw_json.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/raw_json.tsx rename to src/plugins/vis_default_editor/public/components/controls/raw_json.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/rows_or_columns.tsx b/src/plugins/vis_default_editor/public/components/controls/rows_or_columns.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/rows_or_columns.tsx rename to src/plugins/vis_default_editor/public/components/controls/rows_or_columns.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/scale_metrics.tsx b/src/plugins/vis_default_editor/public/components/controls/scale_metrics.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/scale_metrics.tsx rename to src/plugins/vis_default_editor/public/components/controls/scale_metrics.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/size.test.tsx b/src/plugins/vis_default_editor/public/components/controls/size.test.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/size.test.tsx rename to src/plugins/vis_default_editor/public/components/controls/size.test.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/size.tsx b/src/plugins/vis_default_editor/public/components/controls/size.tsx similarity index 98% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/size.tsx rename to src/plugins/vis_default_editor/public/components/controls/size.tsx index 824ec4aeb253c..9f55eb9212dee 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/size.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/size.tsx @@ -48,7 +48,7 @@ function SizeParamEditor({ useEffect(() => { setValidity(isValid); - }, [isValid]); + }, [isValid, setValidity]); return ( { setValidity(isValid); - }, [isValid]); + }, [isValid, setValidity]); const onChange = useCallback(ev => setValue(ev.target.value), [setValue]); diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/sub_agg.tsx b/src/plugins/vis_default_editor/public/components/controls/sub_agg.tsx similarity index 97% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/sub_agg.tsx rename to src/plugins/vis_default_editor/public/components/controls/sub_agg.tsx index c9f53a68b3e83..ee0fbd8532ce9 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/sub_agg.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/sub_agg.tsx @@ -20,7 +20,7 @@ import React, { useEffect } from 'react'; import { EuiSpacer } from '@elastic/eui'; -import { AggParamType, IAggConfig, AggGroupNames } from '../../../../../../plugins/data/public'; +import { AggParamType, IAggConfig, AggGroupNames } from '../../../../data/public'; import { useSubAggParamsHandlers } from './utils'; import { AggParamEditorProps } from '../agg_param_props'; import { DefaultEditorAggParams } from '../agg_params'; @@ -29,7 +29,6 @@ function SubAggParamEditor({ agg, aggParam, formIsTouched, - value, metricAggs, state, setValue, @@ -44,7 +43,7 @@ function SubAggParamEditor({ } else if (!agg.params.customMetric) { setValue(aggParam.makeAgg(agg)); } - }, [value, metricAggs]); + }, [metricAggs, agg, setValue, aggParam]); const { onAggTypeChange, setAggParamValue } = useSubAggParamsHandlers( agg, diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/sub_metric.tsx b/src/plugins/vis_default_editor/public/components/controls/sub_metric.tsx similarity index 96% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/sub_metric.tsx rename to src/plugins/vis_default_editor/public/components/controls/sub_metric.tsx index ead3f8bb00623..361eeba9abdbf 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/sub_metric.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/sub_metric.tsx @@ -17,11 +17,12 @@ * under the License. */ -import React, { useEffect } from 'react'; +import React from 'react'; import { EuiFormLabel, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { useMount } from 'react-use'; -import { AggParamType, IAggConfig, AggGroupNames } from '../../../../../../plugins/data/public'; +import { AggParamType, IAggConfig, AggGroupNames } from '../../../../data/public'; import { useSubAggParamsHandlers } from './utils'; import { AggParamEditorProps } from '../agg_param_props'; import { DefaultEditorAggParams } from '../agg_params'; @@ -48,13 +49,13 @@ function SubMetricParamEditor({ const aggTitle = type === 'customMetric' ? metricTitle : bucketTitle; const aggGroup = type === 'customMetric' ? AggGroupNames.Metrics : AggGroupNames.Buckets; - useEffect(() => { + useMount(() => { if (agg.params[type]) { setValue(agg.params[type]); } else { setValue(aggParam.makeAgg(agg)); } - }, []); + }); const { onAggTypeChange, setAggParamValue } = useSubAggParamsHandlers( agg, diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/switch.tsx b/src/plugins/vis_default_editor/public/components/controls/switch.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/switch.tsx rename to src/plugins/vis_default_editor/public/components/controls/switch.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/test_utils.ts b/src/plugins/vis_default_editor/public/components/controls/test_utils.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/test_utils.ts rename to src/plugins/vis_default_editor/public/components/controls/test_utils.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/time_interval.tsx b/src/plugins/vis_default_editor/public/components/controls/time_interval.tsx similarity index 98% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/time_interval.tsx rename to src/plugins/vis_default_editor/public/components/controls/time_interval.tsx index 971a62faf7d7c..4af41f67bc524 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/controls/time_interval.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/time_interval.tsx @@ -23,7 +23,7 @@ import { EuiFormRow, EuiIconTip, EuiComboBox, EuiComboBoxOptionOption } from '@e import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { search, AggParamOption } from '../../../../../../plugins/data/public'; +import { search, AggParamOption } from '../../../../data/public'; import { AggParamEditorProps } from '../agg_param_props'; const { parseEsInterval, InvalidEsCalendarIntervalError } = search.aggs; @@ -161,7 +161,7 @@ function TimeIntervalParamEditor({ useEffect(() => { setValidity(isValid); - }, [isValid]); + }, [isValid, setValidity]); return ( { setValidity(isValid); - }, [isValid]); + }, [isValid, setValidity]); useEffect(() => { if (isFirstRun.current) { @@ -102,7 +102,7 @@ export function TopAggregateParamEditor({ if (filteredOptions.length === 1) { setValue(aggParam.options.find(opt => opt.value === filteredOptions[0].value)); } - }, [fieldType]); + }, [aggParam.options, fieldType, filteredOptions, setValue, value]); const handleChange = (event: React.ChangeEvent) => { if (event.target.value === emptyValue.value) { diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/top_field.tsx b/src/plugins/vis_default_editor/public/components/controls/top_field.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/top_field.tsx rename to src/plugins/vis_default_editor/public/components/controls/top_field.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/top_size.tsx b/src/plugins/vis_default_editor/public/components/controls/top_size.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/top_size.tsx rename to src/plugins/vis_default_editor/public/components/controls/top_size.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/top_sort_field.tsx b/src/plugins/vis_default_editor/public/components/controls/top_sort_field.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/top_sort_field.tsx rename to src/plugins/vis_default_editor/public/components/controls/top_sort_field.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/use_geocentroid.tsx b/src/plugins/vis_default_editor/public/components/controls/use_geocentroid.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/use_geocentroid.tsx rename to src/plugins/vis_default_editor/public/components/controls/use_geocentroid.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/agg_utils.ts b/src/plugins/vis_default_editor/public/components/controls/utils/agg_utils.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/agg_utils.ts rename to src/plugins/vis_default_editor/public/components/controls/utils/agg_utils.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/index.ts b/src/plugins/vis_default_editor/public/components/controls/utils/index.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/index.ts rename to src/plugins/vis_default_editor/public/components/controls/utils/index.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/inline_comp_wrapper.tsx b/src/plugins/vis_default_editor/public/components/controls/utils/inline_comp_wrapper.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/inline_comp_wrapper.tsx rename to src/plugins/vis_default_editor/public/components/controls/utils/inline_comp_wrapper.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/strings/comma_separated_list.test.ts b/src/plugins/vis_default_editor/public/components/controls/utils/strings/comma_separated_list.test.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/strings/comma_separated_list.test.ts rename to src/plugins/vis_default_editor/public/components/controls/utils/strings/comma_separated_list.test.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/strings/comma_separated_list.ts b/src/plugins/vis_default_editor/public/components/controls/utils/strings/comma_separated_list.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/strings/comma_separated_list.ts rename to src/plugins/vis_default_editor/public/components/controls/utils/strings/comma_separated_list.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/strings/index.ts b/src/plugins/vis_default_editor/public/components/controls/utils/strings/index.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/strings/index.ts rename to src/plugins/vis_default_editor/public/components/controls/utils/strings/index.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/strings/prose.test.ts b/src/plugins/vis_default_editor/public/components/controls/utils/strings/prose.test.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/strings/prose.test.ts rename to src/plugins/vis_default_editor/public/components/controls/utils/strings/prose.test.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/strings/prose.ts b/src/plugins/vis_default_editor/public/components/controls/utils/strings/prose.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/strings/prose.ts rename to src/plugins/vis_default_editor/public/components/controls/utils/strings/prose.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/use_handlers.ts b/src/plugins/vis_default_editor/public/components/controls/utils/use_handlers.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/controls/utils/use_handlers.ts rename to src/plugins/vis_default_editor/public/components/controls/utils/use_handlers.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/controls.tsx b/src/plugins/vis_default_editor/public/components/sidebar/controls.tsx similarity index 98% rename from src/legacy/core_plugins/vis_default_editor/public/components/sidebar/controls.tsx rename to src/plugins/vis_default_editor/public/components/sidebar/controls.tsx index 18b445b4a26db..db9d7d9e3316a 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/controls.tsx +++ b/src/plugins/vis_default_editor/public/components/sidebar/controls.tsx @@ -30,7 +30,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { useDebounce } from 'react-use'; -import { Vis } from '../../../../../../plugins/visualizations/public'; +import { Vis } from 'src/plugins/visualizations/public'; import { discardChanges, EditorAction } from './state'; interface DefaultEditorControlsProps { diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/data_tab.tsx b/src/plugins/vis_default_editor/public/components/sidebar/data_tab.tsx similarity index 97% rename from src/legacy/core_plugins/vis_default_editor/public/components/sidebar/data_tab.tsx rename to src/plugins/vis_default_editor/public/components/sidebar/data_tab.tsx index 0c967723db8e7..0466c64541e23 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/data_tab.tsx +++ b/src/plugins/vis_default_editor/public/components/sidebar/data_tab.tsx @@ -26,7 +26,8 @@ import { IAggConfig, IMetricAggType, search, -} from '../../../../../../plugins/data/public'; + TimeRange, +} from '../../../../data/public'; import { DefaultEditorAggGroup } from '../agg_group'; import { EditorAction, @@ -39,7 +40,6 @@ import { } from './state'; import { AddSchema, ReorderAggs, DefaultEditorAggCommonProps } from '../agg_common_props'; import { ISchemas } from '../../schemas'; -import { TimeRange } from '../../../../../../plugins/data/public'; import { EditorVisState } from './state/reducers'; export interface DefaultEditorDataTabProps { diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/index.ts b/src/plugins/vis_default_editor/public/components/sidebar/index.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/sidebar/index.ts rename to src/plugins/vis_default_editor/public/components/sidebar/index.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/navbar.tsx b/src/plugins/vis_default_editor/public/components/sidebar/navbar.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/sidebar/navbar.tsx rename to src/plugins/vis_default_editor/public/components/sidebar/navbar.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/sidebar.tsx b/src/plugins/vis_default_editor/public/components/sidebar/sidebar.tsx similarity index 96% rename from src/legacy/core_plugins/vis_default_editor/public/components/sidebar/sidebar.tsx rename to src/plugins/vis_default_editor/public/components/sidebar/sidebar.tsx index b24486a12fd24..9dfeae1815d1a 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/sidebar.tsx +++ b/src/plugins/vis_default_editor/public/components/sidebar/sidebar.tsx @@ -23,16 +23,15 @@ import { i18n } from '@kbn/i18n'; import { keyCodes, EuiButtonIcon, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { EventEmitter } from 'events'; -import { Vis } from 'src/plugins/visualizations/public'; +import { Vis, PersistedState } from 'src/plugins/visualizations/public'; +import { SavedSearch } from 'src/plugins/discover/public'; +import { TimeRange } from 'src/plugins/data/public'; import { DefaultEditorNavBar, OptionTab } from './navbar'; import { DefaultEditorControls } from './controls'; import { setStateParamValue, useEditorReducer, useEditorFormState, discardChanges } from './state'; import { DefaultEditorAggCommonProps } from '../agg_common_props'; import { SidebarTitle } from './sidebar_title'; -import { PersistedState } from '../../../../../../plugins/visualizations/public'; -import { SavedSearch } from '../../../../../../plugins/discover/public'; import { Schema } from '../../schemas'; -import { TimeRange } from '../../../../../../plugins/data/public'; interface DefaultEditorSideBarProps { isCollapsed: boolean; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/sidebar_title.tsx b/src/plugins/vis_default_editor/public/components/sidebar/sidebar_title.tsx similarity index 97% rename from src/legacy/core_plugins/vis_default_editor/public/components/sidebar/sidebar_title.tsx rename to src/plugins/vis_default_editor/public/components/sidebar/sidebar_title.tsx index fb63a598a4fae..c9f83e5b474a6 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/sidebar_title.tsx +++ b/src/plugins/vis_default_editor/public/components/sidebar/sidebar_title.tsx @@ -35,8 +35,8 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { Vis } from '../../../../../../plugins/visualizations/public'; -import { SavedSearch } from '../../../../../../plugins/discover/public'; +import { Vis } from 'src/plugins/visualizations/public'; +import { SavedSearch } from 'src/plugins/discover/public'; interface LinkedSearchProps { savedSearch: SavedSearch; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/actions.ts b/src/plugins/vis_default_editor/public/components/sidebar/state/actions.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/actions.ts rename to src/plugins/vis_default_editor/public/components/sidebar/state/actions.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/constants.ts b/src/plugins/vis_default_editor/public/components/sidebar/state/constants.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/constants.ts rename to src/plugins/vis_default_editor/public/components/sidebar/state/constants.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/editor_form_state.ts b/src/plugins/vis_default_editor/public/components/sidebar/state/editor_form_state.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/editor_form_state.ts rename to src/plugins/vis_default_editor/public/components/sidebar/state/editor_form_state.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/index.ts b/src/plugins/vis_default_editor/public/components/sidebar/state/index.ts similarity index 96% rename from src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/index.ts rename to src/plugins/vis_default_editor/public/components/sidebar/state/index.ts index d39d6d07b32d2..1bfa100cbd0f7 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/index.ts +++ b/src/plugins/vis_default_editor/public/components/sidebar/state/index.ts @@ -24,7 +24,7 @@ import { Vis } from 'src/plugins/visualizations/public'; import { createEditorStateReducer, initEditorState, EditorVisState } from './reducers'; import { EditorStateActionTypes } from './constants'; import { EditorAction } from './actions'; -import { useKibana } from '../../../../../../../plugins/kibana_react/public'; +import { useKibana } from '../../../../../kibana_react/public'; import { VisDefaultEditorKibanaServices } from '../../../types'; export * from './editor_form_state'; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/reducers.ts b/src/plugins/vis_default_editor/public/components/sidebar/state/reducers.ts similarity index 99% rename from src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/reducers.ts rename to src/plugins/vis_default_editor/public/components/sidebar/state/reducers.ts index b9f89cebd8bf3..4e7a2904584da 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/components/sidebar/state/reducers.ts +++ b/src/plugins/vis_default_editor/public/components/sidebar/state/reducers.ts @@ -20,7 +20,7 @@ import { cloneDeep } from 'lodash'; import { Vis } from 'src/plugins/visualizations/public'; -import { AggGroupNames, DataPublicPluginStart } from '../../../../../../../plugins/data/public'; +import { AggGroupNames, DataPublicPluginStart } from '../../../../../data/public'; import { EditorStateActionTypes } from './constants'; import { getEnabledMetricAggsCount } from '../../agg_group_helper'; import { EditorAction } from './actions'; diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/utils/editor_config.ts b/src/plugins/vis_default_editor/public/components/utils/editor_config.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/utils/editor_config.ts rename to src/plugins/vis_default_editor/public/components/utils/editor_config.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/components/utils/index.ts b/src/plugins/vis_default_editor/public/components/utils/index.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/components/utils/index.ts rename to src/plugins/vis_default_editor/public/components/utils/index.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/default_editor.tsx b/src/plugins/vis_default_editor/public/default_editor.tsx similarity index 94% rename from src/legacy/core_plugins/vis_default_editor/public/default_editor.tsx rename to src/plugins/vis_default_editor/public/default_editor.tsx index 899b9c1b5fd6e..f1963b94dcf95 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/default_editor.tsx +++ b/src/plugins/vis_default_editor/public/default_editor.tsx @@ -19,8 +19,8 @@ import React, { useEffect, useRef, useState, useCallback } from 'react'; -import { EditorRenderProps } from '../../kibana/public/visualize/np_ready/types'; -import { PanelsContainer, Panel } from '../../../../plugins/kibana_react/public'; +import { EditorRenderProps } from 'src/legacy/core_plugins/kibana/public/visualize/np_ready/types'; +import { PanelsContainer, Panel } from '../../kibana_react/public'; import './vis_type_agg_filter'; import { DefaultEditorSideBar } from './components/sidebar'; diff --git a/src/legacy/core_plugins/vis_default_editor/public/default_editor_controller.tsx b/src/plugins/vis_default_editor/public/default_editor_controller.tsx similarity index 92% rename from src/legacy/core_plugins/vis_default_editor/public/default_editor_controller.tsx rename to src/plugins/vis_default_editor/public/default_editor_controller.tsx index 58e67b5064da5..798da09f8e30b 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/default_editor_controller.tsx +++ b/src/plugins/vis_default_editor/public/default_editor_controller.tsx @@ -23,9 +23,9 @@ import { i18n } from '@kbn/i18n'; import { EventEmitter } from 'events'; import { EditorRenderProps } from 'src/legacy/core_plugins/kibana/public/visualize/np_ready/types'; -import { Vis, VisualizeEmbeddableContract } from '../../../../plugins/visualizations/public'; -import { Storage } from '../../../../plugins/kibana_utils/public'; -import { KibanaContextProvider } from '../../../../plugins/kibana_react/public'; +import { Vis, VisualizeEmbeddableContract } from 'src/plugins/visualizations/public'; +import { Storage } from '../../kibana_utils/public'; +import { KibanaContextProvider } from '../../kibana_react/public'; import { DefaultEditor } from './default_editor'; import { DefaultEditorDataTab, OptionTab } from './components/sidebar'; diff --git a/src/legacy/core_plugins/vis_default_editor/public/editor_size.ts b/src/plugins/vis_default_editor/public/editor_size.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/editor_size.ts rename to src/plugins/vis_default_editor/public/editor_size.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/index.scss b/src/plugins/vis_default_editor/public/index.scss similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/index.scss rename to src/plugins/vis_default_editor/public/index.scss diff --git a/src/legacy/core_plugins/vis_default_editor/public/index.ts b/src/plugins/vis_default_editor/public/index.ts similarity index 97% rename from src/legacy/core_plugins/vis_default_editor/public/index.ts rename to src/plugins/vis_default_editor/public/index.ts index 156d50f451b57..6c58c6df26b00 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/index.ts +++ b/src/plugins/vis_default_editor/public/index.ts @@ -17,6 +17,8 @@ * under the License. */ +import './index.scss'; + export { DefaultEditorController } from './default_editor_controller'; export { useValidation } from './components/controls/utils'; export { RangesParamEditor, RangeValues } from './components/controls/ranges'; diff --git a/src/legacy/core_plugins/vis_default_editor/public/schemas.ts b/src/plugins/vis_default_editor/public/schemas.ts similarity index 96% rename from src/legacy/core_plugins/vis_default_editor/public/schemas.ts rename to src/plugins/vis_default_editor/public/schemas.ts index 4e632da44afc0..05ba5fa9c9419 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/schemas.ts +++ b/src/plugins/vis_default_editor/public/schemas.ts @@ -21,7 +21,7 @@ import _, { defaults } from 'lodash'; import { Optional } from '@kbn/utility-types'; -import { AggGroupNames, AggParam, IAggGroupNames } from '../../../../plugins/data/public'; +import { AggGroupNames, AggParam, IAggGroupNames } from '../../data/public'; export interface ISchemas { [AggGroupNames.Buckets]: Schema[]; diff --git a/src/legacy/core_plugins/vis_default_editor/public/types.ts b/src/plugins/vis_default_editor/public/types.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/types.ts rename to src/plugins/vis_default_editor/public/types.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/utils.test.ts b/src/plugins/vis_default_editor/public/utils.test.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/utils.test.ts rename to src/plugins/vis_default_editor/public/utils.test.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/utils.ts b/src/plugins/vis_default_editor/public/utils.ts similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/utils.ts rename to src/plugins/vis_default_editor/public/utils.ts diff --git a/src/legacy/core_plugins/vis_default_editor/public/vis_options_props.tsx b/src/plugins/vis_default_editor/public/vis_options_props.tsx similarity index 100% rename from src/legacy/core_plugins/vis_default_editor/public/vis_options_props.tsx rename to src/plugins/vis_default_editor/public/vis_options_props.tsx diff --git a/src/legacy/core_plugins/vis_default_editor/public/vis_type_agg_filter.ts b/src/plugins/vis_default_editor/public/vis_type_agg_filter.ts similarity index 97% rename from src/legacy/core_plugins/vis_default_editor/public/vis_type_agg_filter.ts rename to src/plugins/vis_default_editor/public/vis_type_agg_filter.ts index 3ff212c43e6e8..bf5661f42a9f5 100644 --- a/src/legacy/core_plugins/vis_default_editor/public/vis_type_agg_filter.ts +++ b/src/plugins/vis_default_editor/public/vis_type_agg_filter.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { IAggType, IAggConfig, IndexPattern, search } from '../../../../plugins/data/public'; +import { IAggType, IAggConfig, IndexPattern, search } from '../../data/public'; const { aggTypeFilters, propFilter } = search.aggs; const filterByName = propFilter('name'); diff --git a/src/plugins/visualizations/public/legacy/build_pipeline.ts b/src/plugins/visualizations/public/legacy/build_pipeline.ts index 18af94c919247..f3192ba3da81f 100644 --- a/src/plugins/visualizations/public/legacy/build_pipeline.ts +++ b/src/plugins/visualizations/public/legacy/build_pipeline.ts @@ -94,8 +94,11 @@ const getSchemas = ( const createSchemaConfig = (accessor: number, agg: IAggConfig): SchemaConfig => { if (isDateHistogramBucketAggConfig(agg)) { agg.params.timeRange = timeRange; - const bounds = agg.params.timeRange ? timefilter.calculateBounds(agg.params.timeRange) : null; - agg.buckets.setBounds(agg.fieldIsTimeField() && bounds); + const bounds = + agg.params.timeRange && agg.fieldIsTimeField() + ? timefilter.calculateBounds(agg.params.timeRange) + : undefined; + agg.buckets.setBounds(bounds); agg.buckets.setInterval(agg.params.interval); } diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts index 216defcee9016..8fcb84b19a9be 100644 --- a/src/plugins/visualizations/public/plugin.ts +++ b/src/plugins/visualizations/public/plugin.ts @@ -26,6 +26,7 @@ import { setCapabilities, setHttp, setIndexPatterns, + setSearch, setSavedObjects, setUsageCollector, setFilterManager, @@ -140,6 +141,7 @@ export class VisualizationsPlugin setHttp(core.http); setSavedObjects(core.savedObjects); setIndexPatterns(data.indexPatterns); + setSearch(data.search); setFilterManager(data.query.filterManager); setExpressions(expressions); setUiActions(uiActions); @@ -150,6 +152,7 @@ export class VisualizationsPlugin const savedVisualizationsLoader = createSavedVisLoader({ savedObjectsClient: core.savedObjects.client, indexPatterns: data.indexPatterns, + search: data.search, chrome: core.chrome, overlays: core.overlays, visualizationTypes: types, diff --git a/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts b/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts index bc96e08f4b9da..c99c7a4c2caa1 100644 --- a/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts +++ b/src/plugins/visualizations/public/saved_visualizations/_saved_vis.ts @@ -35,7 +35,7 @@ import { extractReferences, injectReferences } from './saved_visualization_refer import { IIndexPattern, ISearchSource, SearchSource } from '../../../../plugins/data/public'; import { ISavedVis, SerializedVis } from '../types'; import { createSavedSearchesLoader } from '../../../../plugins/discover/public'; -import { getChrome, getOverlays, getIndexPatterns, getSavedObjects } from '../services'; +import { getChrome, getOverlays, getIndexPatterns, getSavedObjects, getSearch } from '../services'; export const convertToSerializedVis = async (savedVis: ISavedVis): Promise => { const { visState } = savedVis; @@ -87,6 +87,7 @@ const getSearchSource = async (inputSearchSource: ISearchSource, savedSearchId?: const savedSearch = await createSavedSearchesLoader({ savedObjectsClient: getSavedObjects().client, indexPatterns: getIndexPatterns(), + search: getSearch(), chrome: getChrome(), overlays: getOverlays(), }).get(savedSearchId); diff --git a/src/plugins/visualizations/public/services.ts b/src/plugins/visualizations/public/services.ts index c4668fa4b0c79..618c61dff176a 100644 --- a/src/plugins/visualizations/public/services.ts +++ b/src/plugins/visualizations/public/services.ts @@ -63,6 +63,8 @@ export const [getIndexPatterns, setIndexPatterns] = createGetterSetter('Search'); + export const [getUsageCollector, setUsageCollector] = createGetterSetter( 'UsageCollection' ); diff --git a/test/functional/apps/discover/_discover.js b/test/functional/apps/discover/_discover.js index 850b2773b5025..4f357e2993b30 100644 --- a/test/functional/apps/discover/_discover.js +++ b/test/functional/apps/discover/_discover.js @@ -44,7 +44,6 @@ export default function({ getService, getPageObjects }) { }); describe('query', function() { - this.tags(['skipFirefox']); const queryName1 = 'Query # 1'; it('should show correct time range string by timepicker', async function() { @@ -100,9 +99,10 @@ export default function({ getService, getPageObjects }) { const newDurationHours = await PageObjects.timePicker.getTimeDurationInHours(); expect(Math.round(newDurationHours)).to.be(25); const rowData = await PageObjects.discover.getDocTableField(1); + log.debug(`The first timestamp value in doc table: ${rowData}`); expect(Date.parse(rowData)).to.be.within( - Date.parse('Sep 20, 2015 @ 22:00:00.000'), - Date.parse('Sep 20, 2015 @ 23:30:00.000') + Date.parse('Sep 20, 2015 @ 21:30:00.000'), + Date.parse('Sep 20, 2015 @ 23:00:00.000') ); }); diff --git a/test/functional/apps/discover/_indexpattern_without_timefield.ts b/test/functional/apps/discover/_indexpattern_without_timefield.ts new file mode 100644 index 0000000000000..87a2da7e44a5e --- /dev/null +++ b/test/functional/apps/discover/_indexpattern_without_timefield.ts @@ -0,0 +1,52 @@ +/* + * 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. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function({ getService, getPageObjects }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const PageObjects = getPageObjects(['common', 'timePicker', 'discover']); + + describe('indexpattern without timefield', function() { + before(async function() { + await esArchiver.loadIfNeeded('index_pattern_without_timefield'); + }); + + beforeEach(async function() { + await PageObjects.common.navigateToApp('discover'); + await PageObjects.discover.selectIndexPattern('without-timefield'); + }); + + after(async function unloadMakelogs() { + await esArchiver.unload('index_pattern_without_timefield'); + }); + + it('should not display a timepicker', async function() { + const timepickerExists = await PageObjects.timePicker.timePickerExists(); + expect(timepickerExists).to.be(false); + }); + + it('should display a timepicker after switching to an index pattern with timefield', async function() { + expect(await PageObjects.timePicker.timePickerExists()).to.be(false); + await PageObjects.discover.selectIndexPattern('with-timefield'); + expect(await PageObjects.timePicker.timePickerExists()).to.be(true); + }); + }); +} diff --git a/test/functional/apps/discover/index.js b/test/functional/apps/discover/index.js index 582c979a194f4..50f140b99aa1a 100644 --- a/test/functional/apps/discover/index.js +++ b/test/functional/apps/discover/index.js @@ -47,5 +47,6 @@ export default function({ getService, loadTestFile }) { loadTestFile(require.resolve('./_doc_navigation')); loadTestFile(require.resolve('./_date_nanos')); loadTestFile(require.resolve('./_date_nanos_mixed')); + loadTestFile(require.resolve('./_indexpattern_without_timefield')); }); } diff --git a/test/functional/apps/management/_mgmt_import_saved_objects.js b/test/functional/apps/management/_mgmt_import_saved_objects.js index 53b7e7062ee2d..2f9d9f9bfb178 100644 --- a/test/functional/apps/management/_mgmt_import_saved_objects.js +++ b/test/functional/apps/management/_mgmt_import_saved_objects.js @@ -35,6 +35,7 @@ export default function({ getService, getPageObjects }) { afterEach(async function() { await esArchiver.unload('discover'); + await esArchiver.load('empty_kibana'); }); it('should import saved objects mgmt', async function() { diff --git a/test/functional/config.edge.js b/test/functional/config.edge.js new file mode 100644 index 0000000000000..ed68b41e8c89a --- /dev/null +++ b/test/functional/config.edge.js @@ -0,0 +1,34 @@ +/* + * 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 default async function({ readConfigFile }) { + const defaultConfig = await readConfigFile(require.resolve('./config')); + + return { + ...defaultConfig.getAll(), + + browser: { + type: 'msedge', + }, + + junit: { + reportName: 'MS Chromium Edge UI Functional Tests', + }, + }; +} diff --git a/test/functional/fixtures/es_archiver/index_pattern_without_timefield/data.json b/test/functional/fixtures/es_archiver/index_pattern_without_timefield/data.json new file mode 100644 index 0000000000000..9493408a30040 --- /dev/null +++ b/test/functional/fixtures/es_archiver/index_pattern_without_timefield/data.json @@ -0,0 +1,65 @@ +{ + "type": "doc", + "value": { + "id": "index-pattern:without-timefield", + "index": ".kibana", + "source": { + "index-pattern": { + "fields": "[{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false}]", + "title": "without-timefield" + }, + "type": "index-pattern" + } + } +} + +{ + "type": "doc", + "value": { + "id": "AU_x3-TaGFA8no6QjiSJ", + "index": "without-timefield", + "source": { + "@message" : "5", + "@timestamp": "2019-09-22T23:50:13.253Z", + "referer": "http://twitter.com/error/takuya-onishi", + "request": "/uploads/dafydd-williams.jpg", + "response": "200", + "type": "apache", + "url": "https://media-for-the-masses.theacademyofperformingartsandscience.org/uploads/dafydd-williams.jpg" + } + } +} + +{ + "type": "doc", + "value": { + "id": "index-pattern:with-timefield", + "index": ".kibana", + "source": { + "index-pattern": { + "fields": "[{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":true,\"doc_values\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"indexed\":true,\"analyzed\":false,\"doc_values\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"indexed\":false,\"analyzed\":false,\"doc_values\":false}]", + "title": "with-timefield", + "timeFieldName": "@timestamp" + }, + "type": "index-pattern" + } + } +} + +{ + "type": "doc", + "value": { + "id": "AU_x3-TaGFA8no6QjiSJ", + "index": "with-timefield", + "source": { + "@message" : "5", + "@timestamp": "2019-09-22T23:50:13.253Z", + "referer": "http://twitter.com/error/takuya-onishi", + "request": "/uploads/dafydd-williams.jpg", + "response": "200", + "type": "apache", + "url": "https://media-for-the-masses.theacademyofperformingartsandscience.org/uploads/dafydd-williams.jpg" + } + } +} + diff --git a/test/functional/fixtures/es_archiver/index_pattern_without_timefield/mappings.json b/test/functional/fixtures/es_archiver/index_pattern_without_timefield/mappings.json new file mode 100644 index 0000000000000..0096111923951 --- /dev/null +++ b/test/functional/fixtures/es_archiver/index_pattern_without_timefield/mappings.json @@ -0,0 +1,39 @@ +{ + "type": "index", + "value": { + "index": "without-timefield", + "mappings": { + "properties": { + "@timestamp": { + "type": "date" + } + } + }, + "settings": { + "index": { + "number_of_replicas": "0", + "number_of_shards": "1" + } + } + } +} + +{ + "type": "index", + "value": { + "index": "with-timefield", + "mappings": { + "properties": { + "@timestamp": { + "type": "date" + } + } + }, + "settings": { + "index": { + "number_of_replicas": "0", + "number_of_shards": "1" + } + } + } +} diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts index de4917ef2b1b3..f06baeb7a4167 100644 --- a/test/functional/page_objects/common_page.ts +++ b/test/functional/page_objects/common_page.ts @@ -43,42 +43,10 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo appConfig: {}; ensureCurrentUrl: boolean; shouldLoginIfPrompted: boolean; - shouldAcceptAlert: boolean; useActualUrl: boolean; } class CommonPage { - /** - * Navigates the browser window to provided URL - * @param url URL - * @param shouldAcceptAlert pass 'true' if browser alert should be accepted - */ - private static async navigateToUrlAndHandleAlert(url: string, shouldAcceptAlert: boolean) { - log.debug('Navigate to: ' + url); - try { - await browser.get(url); - } catch (navigationError) { - log.debug('Error navigating to url'); - const alert = await browser.getAlert(); - if (alert && alert.accept) { - if (shouldAcceptAlert) { - log.debug('Should accept alert'); - try { - await alert.accept(); - } catch (alertException) { - log.debug('Error accepting alert'); - throw alertException; - } - } else { - log.debug('Will not accept alert'); - throw navigationError; - } - } else { - throw navigationError; - } - } - } - /** * Returns Kibana host URL */ @@ -127,13 +95,7 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo } private async navigate(navigateProps: NavigateProps) { - const { - appConfig, - ensureCurrentUrl, - shouldLoginIfPrompted, - shouldAcceptAlert, - useActualUrl, - } = navigateProps; + const { appConfig, ensureCurrentUrl, shouldLoginIfPrompted, useActualUrl } = navigateProps; const appUrl = getUrl.noAuth(config.get('servers.kibana'), appConfig); await retry.try(async () => { @@ -141,7 +103,11 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo log.debug(`navigateToActualUrl ${appUrl}`); await browser.get(appUrl); } else { - await CommonPage.navigateToUrlAndHandleAlert(appUrl, shouldAcceptAlert); + log.debug(`navigateToUrl ${appUrl}`); + await browser.get(appUrl); + // accept alert if it pops up + const alert = await browser.getAlert(); + await alert?.accept(); } const currentUrl = shouldLoginIfPrompted @@ -167,7 +133,6 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo basePath = '', ensureCurrentUrl = true, shouldLoginIfPrompted = true, - shouldAcceptAlert = true, useActualUrl = false, } = {} ) { @@ -180,7 +145,6 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo appConfig, ensureCurrentUrl, shouldLoginIfPrompted, - shouldAcceptAlert, useActualUrl, }); } @@ -200,7 +164,6 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo basePath = '', ensureCurrentUrl = true, shouldLoginIfPrompted = true, - shouldAcceptAlert = true, useActualUrl = true, } = {} ) { @@ -214,7 +177,6 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo appConfig, ensureCurrentUrl, shouldLoginIfPrompted, - shouldAcceptAlert, useActualUrl, }); } @@ -228,18 +190,12 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo async navigateToActualUrl( appName: string, hash?: string, - { - basePath = '', - ensureCurrentUrl = true, - shouldLoginIfPrompted = true, - shouldAcceptAlert = true, - } = {} + { basePath = '', ensureCurrentUrl = true, shouldLoginIfPrompted = true } = {} ) { await this.navigateToUrl(appName, hash, { basePath, ensureCurrentUrl, shouldLoginIfPrompted, - shouldAcceptAlert, useActualUrl: true, }); } @@ -252,7 +208,7 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo async navigateToApp( appName: string, - { basePath = '', shouldLoginIfPrompted = true, shouldAcceptAlert = true, hash = '' } = {} + { basePath = '', shouldLoginIfPrompted = true, hash = '' } = {} ) { let appUrl: string; if (config.has(['apps', appName])) { @@ -274,7 +230,11 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo await retry.tryForTime(defaultTryTimeout * 2, async () => { let lastUrl = await retry.try(async () => { // since we're using hash URLs, always reload first to force re-render - await CommonPage.navigateToUrlAndHandleAlert(appUrl, shouldAcceptAlert); + log.debug('navigate to: ' + appUrl); + await browser.get(appUrl); + // accept alert if it pops up + const alert = await browser.getAlert(); + await alert?.accept(); await this.sleep(700); log.debug('returned from get, calling refresh'); await browser.refresh(); diff --git a/test/functional/page_objects/discover_page.ts b/test/functional/page_objects/discover_page.ts index 2377c32a80b5b..00bf87621864a 100644 --- a/test/functional/page_objects/discover_page.ts +++ b/test/functional/page_objects/discover_page.ts @@ -138,7 +138,7 @@ export function DiscoverPageProvider({ getService, getPageObjects }: FtrProvider await browser .getActions() - .move({ x: 200, y: 20, origin: el._webElement }) + .move({ x: 0, y: 20, origin: el._webElement }) .click() .perform(); } @@ -147,8 +147,8 @@ export function DiscoverPageProvider({ getService, getPageObjects }: FtrProvider const el = await elasticChart.getCanvas(); await browser.dragAndDrop( - { location: el, offset: { x: 200, y: 20 } }, - { location: el, offset: { x: 400, y: 30 } } + { location: el, offset: { x: -300, y: 20 } }, + { location: el, offset: { x: -100, y: 30 } } ); } diff --git a/test/functional/services/browser.ts b/test/functional/services/browser.ts index 5017947e95d03..13d2365c07191 100644 --- a/test/functional/services/browser.ts +++ b/test/functional/services/browser.ts @@ -47,7 +47,9 @@ export async function BrowserProvider({ getService }: FtrProviderContext) { */ public readonly browserType: string = browserType; - public readonly isChrome: boolean = browserType === Browsers.Chrome; + public readonly isChromium: boolean = [Browsers.Chrome, Browsers.ChromiumEdge].includes( + browserType + ); public readonly isFirefox: boolean = browserType === Browsers.Firefox; diff --git a/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts b/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts index 157918df874c8..8b57ecd3c8235 100644 --- a/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts +++ b/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts @@ -55,6 +55,7 @@ export class WebElementWrapper { private driver: WebDriver = this.webDriver.driver; private Keys = Key; public isW3CEnabled: boolean = (this.webDriver.driver as any).executor_.w3c === true; + public isChromium: boolean = [Browsers.Chrome, Browsers.ChromiumEdge].includes(this.browserType); public static create( webElement: WebElement | WebElementWrapper, @@ -63,7 +64,7 @@ export class WebElementWrapper { timeout: number, fixedHeaderHeight: number, logger: ToolingLog, - browserType: string + browserType: Browsers ): WebElementWrapper { if (webElement instanceof WebElementWrapper) { return webElement; @@ -87,7 +88,7 @@ export class WebElementWrapper { private timeout: number, private fixedHeaderHeight: number, private logger: ToolingLog, - private browserType: string + private browserType: Browsers ) {} private async _findWithCustomTimeout( @@ -243,7 +244,7 @@ export class WebElementWrapper { return this.clearValueWithKeyboard(); } await this.retryCall(async function clearValue(wrapper) { - if (wrapper.browserType === Browsers.Chrome || options.withJS) { + if (wrapper.isChromium || options.withJS) { // https://bugs.chromium.org/p/chromedriver/issues/detail?id=2702 await wrapper.driver.executeScript(`arguments[0].value=''`, wrapper._webElement); } else { @@ -275,7 +276,7 @@ export class WebElementWrapper { await delay(100); } } else { - if (this.browserType === Browsers.Chrome) { + if (this.isChromium) { // https://bugs.chromium.org/p/chromedriver/issues/detail?id=30 await this.retryCall(async function clearValueWithKeyboard(wrapper) { await wrapper.driver.executeScript(`arguments[0].select();`, wrapper._webElement); diff --git a/test/functional/services/remote/browsers.ts b/test/functional/services/remote/browsers.ts index 46d81f1737a55..aa6e364d0a09d 100644 --- a/test/functional/services/remote/browsers.ts +++ b/test/functional/services/remote/browsers.ts @@ -21,4 +21,5 @@ export enum Browsers { Chrome = 'chrome', Firefox = 'firefox', InternetExplorer = 'ie', + ChromiumEdge = 'msedge', } diff --git a/test/functional/services/remote/remote.ts b/test/functional/services/remote/remote.ts index e571a1a7e5551..b0724488cb5db 100644 --- a/test/functional/services/remote/remote.ts +++ b/test/functional/services/remote/remote.ts @@ -64,18 +64,23 @@ export async function RemoteProvider({ getService }: FtrProviderContext) { lifecycle, config.get('browser.logPollingMs') ); + const isW3CEnabled = (driver as any).executor_.w3c; const caps = await driver.getCapabilities(); - const browserVersion = caps.get(isW3CEnabled ? 'browserVersion' : 'version'); + const browserVersion = caps.get( + isW3CEnabled || browserType === Browsers.ChromiumEdge ? 'browserVersion' : 'version' + ); - log.info(`Remote initialized: ${caps.get('browserName')} ${browserVersion}`); + log.info( + `Remote initialized: ${caps.get( + 'browserName' + )} ${browserVersion}, w3c compliance=${isW3CEnabled}, collectingCoverage=${collectCoverage}` + ); - if (browserType === Browsers.Chrome) { + if ([Browsers.Chrome, Browsers.ChromiumEdge].includes(browserType)) { log.info( - `Chromedriver version: ${ - caps.get('chrome').chromedriverVersion - }, w3c=${isW3CEnabled}, codeCoverage=${collectCoverage}` + `${browserType}driver version: ${caps.get(browserType)[`${browserType}driverVersion`]}` ); } diff --git a/test/functional/services/remote/webdriver.ts b/test/functional/services/remote/webdriver.ts index 382543822d8ac..fc0b5bbb787c8 100644 --- a/test/functional/services/remote/webdriver.ts +++ b/test/functional/services/remote/webdriver.ts @@ -31,10 +31,12 @@ import { Builder, Capabilities, By, logging, until } from 'selenium-webdriver'; import chrome from 'selenium-webdriver/chrome'; import firefox from 'selenium-webdriver/firefox'; // @ts-ignore internal modules are not typed +import edge from 'selenium-webdriver/edge'; +import { installDriver } from 'ms-chromium-edge-driver'; +// @ts-ignore internal modules are not typed import { Executor } from 'selenium-webdriver/lib/http'; // @ts-ignore internal modules are not typed import { getLogger } from 'selenium-webdriver/lib/logging'; - import { pollForLogEntry$ } from './poll_for_log_entry'; import { createStdoutSocket } from './create_stdout_stream'; import { preventParallelCalls } from './prevent_parallel_calls'; @@ -63,6 +65,7 @@ Executor.prototype.execute = preventParallelCalls( ); let attemptCounter = 0; +let edgePaths: { driverPath: string | undefined; browserPath: string | undefined }; async function attemptToCreateCommand( log: ToolingLog, browserType: Browsers, @@ -74,6 +77,46 @@ async function attemptToCreateCommand( const buildDriverInstance = async () => { switch (browserType) { + case 'msedge': { + if (edgePaths && edgePaths.browserPath && edgePaths.driverPath) { + const edgeOptions = new edge.Options(); + if (headlessBrowser === '1') { + // @ts-ignore internal modules are not typed + edgeOptions.headless(); + } + // @ts-ignore internal modules are not typed + edgeOptions.setEdgeChromium(true); + // @ts-ignore internal modules are not typed + edgeOptions.setBinaryPath(edgePaths.browserPath); + const session = await new Builder() + .forBrowser('MicrosoftEdge') + .setEdgeOptions(edgeOptions) + .setEdgeService(new edge.ServiceBuilder(edgePaths.driverPath)) + .build(); + return { + session, + consoleLog$: pollForLogEntry$( + session, + logging.Type.BROWSER, + logPollingMs, + lifecycle.cleanup.after$ + ).pipe( + takeUntil(lifecycle.cleanup.after$), + map(({ message, level: { name: level } }) => ({ + message: message.replace(/\\n/g, '\n'), + level, + })) + ), + }; + } else { + throw new Error( + `Chromium Edge session requires browser or driver path to be defined: ${JSON.stringify( + edgePaths + )}` + ); + } + } + case 'chrome': { const chromeCapabilities = Capabilities.chrome(); const chromeOptions = [ @@ -107,9 +150,10 @@ async function attemptToCreateCommand( chromeOptions.push('headless', 'disable-gpu', 'remote-debugging-port=9222'); } chromeCapabilities.set('goog:chromeOptions', { - w3c: false, + w3c: true, args: chromeOptions, }); + chromeCapabilities.set('unexpectedAlertBehaviour', 'accept'); chromeCapabilities.set('goog:loggingPrefs', { browser: 'ALL' }); const session = await new Builder() @@ -264,6 +308,11 @@ export async function initWebDriver( log.verbose(entry.message); }); + // download Edge driver only in case of usage + if (browserType === Browsers.ChromiumEdge) { + edgePaths = await installDriver(); + } + return await Promise.race([ (async () => { await delay(2 * MINUTE); diff --git a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_editor.tsx b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_editor.tsx index d3f66d708603c..6c430e34d5e29 100644 --- a/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_editor.tsx +++ b/test/plugin_functional/plugins/kbn_tp_custom_visualizations/public/self_changing_vis/self_changing_editor.tsx @@ -20,7 +20,7 @@ import React from 'react'; import { EuiFieldNumber, EuiFormRow } from '@elastic/eui'; -import { VisOptionsProps } from '../../../../../../src/legacy/core_plugins/vis_default_editor/public/vis_options_props'; +import { VisOptionsProps } from 'src/plugins/vis_default_editor/public/vis_options_props'; interface CounterParams { counter: number; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap index 205a303bcf47b..afa0cb51cd108 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap +++ b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/__test__/__snapshots__/List.test.tsx.snap @@ -11,6 +11,12 @@ exports[`ErrorGroupOverview -> List should render empty state 1`] = ` "sortable": false, "width": "96px", }, + Object { + "field": "type", + "name": "Type", + "render": [Function], + "sortable": false, + }, Object { "field": "message", "name": "Error message and culprit", @@ -142,7 +148,28 @@ exports[`ErrorGroupOverview -> List should render empty state 1`] = ` +
+ + Type + +
+ + List should render empty state 1`] = ` List should render empty state 1`] = ` aria-live="polite" aria-sort="descending" className="euiTableHeaderCell" - data-test-subj="tableHeaderCell_occurrenceCount_3" + data-test-subj="tableHeaderCell_occurrenceCount_4" role="columnheader" scope="col" style={ @@ -225,7 +252,7 @@ exports[`ErrorGroupOverview -> List should render empty state 1`] = ` aria-live="polite" aria-sort="none" className="euiTableHeaderCell" - data-test-subj="tableHeaderCell_latestOccurrenceAt_4" + data-test-subj="tableHeaderCell_latestOccurrenceAt_5" role="columnheader" scope="col" style={ @@ -264,7 +291,7 @@ exports[`ErrorGroupOverview -> List should render empty state 1`] = ` > List should render with data 1`] = ` font-family: "Roboto Mono",Consolas,Menlo,Courier,monospace; } +.c2 { + max-width: 100%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + .c1 { max-width: 100%; white-space: nowrap; @@ -301,7 +335,7 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = ` text-overflow: ellipsis; } -.c2 { +.c3 { font-family: "Roboto Mono",Consolas,Menlo,Courier,monospace; font-size: 16px; max-width: 100%; @@ -310,7 +344,7 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = ` text-overflow: ellipsis; } -.c3 { +.c4 { font-family: "Roboto Mono",Consolas,Menlo,Courier,monospace; } @@ -324,6 +358,12 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = ` "sortable": false, "width": "96px", }, + Object { + "field": "type", + "name": "Type", + "render": [Function], + "sortable": false, + }, Object { "field": "message", "name": "Error message and culprit", @@ -486,7 +526,28 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = ` +
+ + Type + +
+ + List should render with data 1`] = ` List should render with data 1`] = ` aria-live="polite" aria-sort="descending" className="euiTableHeaderCell" - data-test-subj="tableHeaderCell_occurrenceCount_3" + data-test-subj="tableHeaderCell_occurrenceCount_4" role="columnheader" scope="col" style={ @@ -569,7 +630,7 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = ` aria-live="polite" aria-sort="none" className="euiTableHeaderCell" - data-test-subj="tableHeaderCell_latestOccurrenceAt_4" + data-test-subj="tableHeaderCell_latestOccurrenceAt_5" role="columnheader" scope="col" style={ @@ -642,6 +703,49 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = ` + +
+ Type +
+ + List should render with data 1`] = ` className="" >
List should render with data 1`] = ` serviceName="opbeans-python" > List should render with data 1`] = ` onFocus={[Function]} >
@@ -812,6 +916,49 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = `
+ +
+ Type +
+
+ List should render with data 1`] = ` className="" >
List should render with data 1`] = ` serviceName="opbeans-python" > List should render with data 1`] = ` onFocus={[Function]} >
@@ -982,6 +1129,49 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = `
+ +
+ Type +
+
+ List should render with data 1`] = ` className="" >
List should render with data 1`] = ` serviceName="opbeans-python" > List should render with data 1`] = ` onFocus={[Function]} >
@@ -1152,6 +1342,49 @@ exports[`ErrorGroupOverview -> List should render with data 1`] = `
+ +
+ Type +
+
+ List should render with data 1`] = ` className="" >
List should render with data 1`] = ` serviceName="opbeans-python" > List should render with data 1`] = ` onFocus={[Function]} >
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/index.tsx index b26833c02fe22..250b9a5d188d0 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupOverview/List/index.tsx @@ -23,6 +23,8 @@ import { useUrlParams } from '../../../../hooks/useUrlParams'; import { ManagedTable } from '../../../shared/ManagedTable'; import { ErrorDetailLink } from '../../../shared/Links/apm/ErrorDetailLink'; import { TimestampTooltip } from '../../../shared/TimestampTooltip'; +import { ErrorOverviewLink } from '../../../shared/Links/apm/ErrorOverviewLink'; +import { APMQueryParams } from '../../../shared/Links/url_helpers'; const GroupIdLink = styled(ErrorDetailLink)` font-family: ${fontFamilyCode}; @@ -32,6 +34,10 @@ const MessageAndCulpritCell = styled.div` ${truncate('100%')}; `; +const ErrorLink = styled(ErrorOverviewLink)` + ${truncate('100%')}; +`; + const MessageLink = styled(ErrorDetailLink)` font-family: ${fontFamilyCode}; font-size: ${fontSizes.large}; @@ -48,9 +54,8 @@ interface Props { const ErrorGroupList: React.FC = props => { const { items } = props; - const { - urlParams: { serviceName } - } = useUrlParams(); + const { urlParams } = useUrlParams(); + const { serviceName } = urlParams; if (!serviceName) { throw new Error('Service name is required'); @@ -73,6 +78,29 @@ const ErrorGroupList: React.FC = props => { ); } }, + { + name: i18n.translate('xpack.apm.errorsTable.typeColumnLabel', { + defaultMessage: 'Type' + }), + field: 'type', + sortable: false, + render: (type: string, item: ErrorGroupListAPIResponse[0]) => { + return ( + + {type} + + ); + } + }, { name: i18n.translate( 'xpack.apm.errorsTable.errorMessageAndCulpritColumnLabel', @@ -150,7 +178,7 @@ const ErrorGroupList: React.FC = props => { ) } ], - [serviceName] + [serviceName, urlParams] ); return ( diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx index 46754c8c7cb6b..a8d3b843a1f3d 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.stories.tsx @@ -75,27 +75,17 @@ storiesOf('app/ServiceMap/Cytoscape', module) const cy = cytoscape(); const elements = [ { data: { id: 'default' } }, - { data: { id: 'cache', label: 'cache', 'span.type': 'cache' } }, - { data: { id: 'database', label: 'database', 'span.type': 'db' } }, + { data: { id: 'cache', 'span.type': 'cache' } }, + { data: { id: 'database', 'span.type': 'db' } }, { data: { id: 'elasticsearch', - label: 'elasticsearch', 'span.type': 'db', 'span.subtype': 'elasticsearch' } }, - { - data: { id: 'external', label: 'external', 'span.type': 'external' } - }, - { - data: { - id: 'messaging', - label: 'messaging', - 'span.type': 'messaging' - } - }, - + { data: { id: 'external', 'span.type': 'external' } }, + { data: { id: 'messaging', 'span.type': 'messaging' } }, { data: { id: 'dotnet', @@ -119,11 +109,18 @@ storiesOf('app/ServiceMap/Cytoscape', module) }, { data: { - id: 'js-base', - 'service.name': 'js-base service', + id: 'RUM (js-base)', + 'service.name': 'RUM service', 'agent.name': 'js-base' } }, + { + data: { + id: 'RUM (rum-js)', + 'service.name': 'RUM service', + 'agent.name': 'rum-js' + } + }, { data: { id: 'nodejs', @@ -163,7 +160,8 @@ storiesOf('app/ServiceMap/Cytoscape', module) description={
                     agent.name: {node.data('agent.name') || 'undefined'},
-                    span.type: {node.data('span.type') || 'undefined'}
+                    span.type: {node.data('span.type') || 'undefined'},
+                    span.subtype: {node.data('span.subtype') || 'undefined'}
                   
} icon={ @@ -174,7 +172,7 @@ storiesOf('app/ServiceMap/Cytoscape', module) width={80} /> } - title={node.data('label')} + title={node.data('id')} /> ))} diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx index 7bdc6aebbd9a0..e4b656ae8160d 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/Cytoscape.tsx @@ -14,8 +14,6 @@ import React, { useState } from 'react'; import { debounce } from 'lodash'; -import { isRumAgentName } from '../../../../../../../plugins/apm/common/agent_name'; -import { AGENT_NAME } from '../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; import { animationOptions, cytoscapeOptions, @@ -96,10 +94,15 @@ function getLayoutOptions( } function selectRoots(cy: cytoscape.Core): string[] { - const nodes = cy.nodes(); - const roots = nodes.roots(); - const rumNodes = nodes.filter(node => isRumAgentName(node.data(AGENT_NAME))); - return rumNodes.union(roots).map(node => node.id()); + const bfs = cy.elements().bfs({ + roots: cy.elements().leaves() + }); + const furthestNodeFromLeaves = bfs.path.last(); + return cy + .elements() + .roots() + .union(furthestNodeFromLeaves) + .map(el => el.id()); } export function Cytoscape({ @@ -124,6 +127,12 @@ export function Cytoscape({ // Trigger a custom "data" event when data changes useEffect(() => { if (cy && elements.length > 0) { + const renderedElements = cy.elements('node,edge'); + const latestElementIds = elements.map(el => el.data.id); + const absentElements = renderedElements.filter( + el => !latestElementIds.includes(el.id()) + ); + cy.remove(absentElements); cy.add(elements); cy.trigger('data'); } @@ -162,15 +171,26 @@ export function Cytoscape({ layout.run(); } }; + let layoutstopDelayTimeout: NodeJS.Timeout; const layoutstopHandler: cytoscape.EventHandler = event => { - event.cy.animate({ - ...animationOptions, - center: { - eles: serviceName - ? event.cy.getElementById(serviceName) - : event.cy.collection() + // This 0ms timer is necessary to prevent a race condition + // between the layout finishing rendering and viewport centering + layoutstopDelayTimeout = setTimeout(() => { + if (serviceName) { + event.cy.animate({ + ...animationOptions, + fit: { + eles: event.cy.elements(), + padding: nodeHeight + }, + center: { + eles: event.cy.getElementById(serviceName) + } + }); + } else { + event.cy.fit(undefined, nodeHeight); } - }); + }, 0); }; // debounce hover tracking so it doesn't spam telemetry with redundant events const trackNodeEdgeHover = debounce( @@ -225,6 +245,7 @@ export function Cytoscape({ cy.removeListener('select', 'node', selectHandler); cy.removeListener('unselect', 'node', unselectHandler); } + clearTimeout(layoutstopDelayTimeout); }; }, [cy, height, serviceName, trackApmEvent, width]); diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts index 0438842f7af10..92f66f698f044 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/cytoscapeOptions.ts @@ -83,7 +83,7 @@ const style: cytoscape.Stylesheet[] = [ style: { 'curve-style': 'taxi', // @ts-ignore - 'taxi-direction': 'rightward', + 'taxi-direction': 'auto', 'line-color': lineColor, 'overlay-opacity': 0, 'target-arrow-color': lineColor, diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons.ts b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons.ts index 4925ffba310b5..dd9b48d312725 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons.ts +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/icons.ts @@ -5,11 +5,12 @@ */ import cytoscape from 'cytoscape'; +import { isRumAgentName } from '../../../../../../../plugins/apm/common/agent_name'; import { AGENT_NAME, SERVICE_NAME, - SPAN_TYPE, - SPAN_SUBTYPE + SPAN_SUBTYPE, + SPAN_TYPE } from '../../../../../../../plugins/apm/common/elasticsearch_fieldnames'; import databaseIcon from './icons/database.svg'; import defaultIconImport from './icons/default.svg'; @@ -62,7 +63,12 @@ export function iconForNode(node: cytoscape.NodeSingular) { const type = node.data(SPAN_TYPE); if (node.data(SERVICE_NAME)) { - return serviceIcons[node.data(AGENT_NAME) as string]; + const agentName = node.data(AGENT_NAME); + // RUM can have multiple names. Normalize it + const normalizedAgentName = isRumAgentName(agentName) + ? 'js-base' + : agentName; + return serviceIcons[normalizedAgentName]; } else if (isIE11) { return defaultIcon; } else if ( diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx index b1959e4d68aa4..30c772bf5f634 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/SettingsPage/SettingFormRow.tsx @@ -90,6 +90,7 @@ function FormRow({ onChange( setting.key, diff --git a/x-pack/legacy/plugins/apm/public/components/shared/ErrorRateAlertTrigger/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/ErrorRateAlertTrigger/index.tsx index 9bfc5936a555e..b7e23c2979cb8 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/ErrorRateAlertTrigger/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/ErrorRateAlertTrigger/index.tsx @@ -6,6 +6,7 @@ import React from 'react'; import { EuiFieldNumber } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { isFinite } from 'lodash'; import { ForLastExpression } from '../../../../../../../plugins/triggers_actions_ui/public'; import { ALERT_TYPES_CONFIG } from '../../../../../../../plugins/apm/common/alert_types'; import { ServiceAlertTrigger } from '../ServiceAlertTrigger'; @@ -37,15 +38,17 @@ export function ErrorRateAlertTrigger(props: Props) { ...alertParams }; + const threshold = isFinite(params.threshold) ? params.threshold : ''; + const fields = [ setAlertParams('threshold', parseInt(e.target.value, 10)) diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.tsx b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.tsx index 52befe37ffdae..bd00bcf600ffe 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/CustomLinkSection.tsx @@ -3,14 +3,26 @@ * 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 { EuiLink, EuiText } from '@elastic/eui'; import Mustache from 'mustache'; +import React from 'react'; +import styled from 'styled-components'; import { CustomLink } from '../../../../../../../../plugins/apm/common/custom_link/custom_link_types'; import { Transaction } from '../../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; -import { - SectionLinks, - SectionLink -} from '../../../../../../../../plugins/observability/public'; +import { px, truncate, units } from '../../../../style/variables'; + +const LinkContainer = styled.li` + margin-top: ${px(units.half)}; + &:first-of-type { + margin-top: 0; + } +`; + +const TruncateText = styled(EuiText)` + font-weight: 500; + line-height: ${px(units.unit)}; + ${truncate(px(units.unit * 25))} +`; export const CustomLinkSection = ({ customLinks, @@ -19,7 +31,7 @@ export const CustomLinkSection = ({ customLinks: CustomLink[]; transaction: Transaction; }) => ( - +
    {customLinks.map(link => { let href = link.url; try { @@ -28,13 +40,12 @@ export const CustomLinkSection = ({ // ignores any error that happens } return ( - + + + {link.label} + + ); })} - +
); diff --git a/x-pack/legacy/plugins/canvas/.storybook/webpack.config.js b/x-pack/legacy/plugins/canvas/.storybook/webpack.config.js index 85cb6d45c595d..cc74faeac6a96 100644 --- a/x-pack/legacy/plugins/canvas/.storybook/webpack.config.js +++ b/x-pack/legacy/plugins/canvas/.storybook/webpack.config.js @@ -6,6 +6,7 @@ const path = require('path'); const webpack = require('webpack'); +const { stringifyRequest } = require('loader-utils'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const { DLL_OUTPUT, KIBANA_ROOT } = require('./constants'); @@ -73,7 +74,20 @@ module.exports = async ({ config }) => { path: path.resolve(KIBANA_ROOT, 'src/optimize/postcss.config.js'), }, }, - { loader: 'sass-loader' }, + { + loader: 'sass-loader', + options: { + prependData(loaderContext) { + return `@import ${stringifyRequest( + loaderContext, + path.resolve(KIBANA_ROOT, 'src/legacy/ui/public/styles/_styling_constants.scss') + )};\n`; + }, + sassOptions: { + includePaths: [path.resolve(KIBANA_ROOT, 'node_modules')], + }, + }, + }, ], }); @@ -86,8 +100,9 @@ module.exports = async ({ config }) => { loader: 'css-loader', options: { importLoaders: 2, - modules: true, - localIdentName: '[name]__[local]___[hash:base64:5]', + modules: { + localIdentName: '[name]__[local]___[hash:base64:5]', + }, }, }, { @@ -159,7 +174,11 @@ module.exports = async ({ config }) => { // what require() calls it will execute within the bundle JSON.stringify({ type, modules: extensions[type] || [] }), ].join(''); - }) + }), + + // Mock out libs used by a few componets to avoid loading in kibana_legacy and platform + new webpack.NormalModuleReplacementPlugin(/lib\/notify/, path.resolve(__dirname, '../tasks/mocks/uiNotify')), + new webpack.NormalModuleReplacementPlugin(/lib\/download_workpad/, path.resolve(__dirname, '../tasks/mocks/downloadWorkpad')), ); // Tell Webpack about relevant extensions diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata/demo_rows_types.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata/demo_rows_types.ts new file mode 100644 index 0000000000000..e92dc79fba8c3 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata/demo_rows_types.ts @@ -0,0 +1,10 @@ +/* + * 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 enum DemoRows { + CI = 'ci', + SHIRTS = 'shirts', +} diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata/get_demo_rows.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata/get_demo_rows.ts index 02f8efcfde95d..58a2354b5cf38 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata/get_demo_rows.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata/get_demo_rows.ts @@ -6,14 +6,10 @@ import { cloneDeep } from 'lodash'; import ci from './ci.json'; +import { DemoRows } from './demo_rows_types'; import shirts from './shirts.json'; import { getFunctionErrors } from '../../../../i18n'; -export enum DemoRows { - CI = 'ci', - SHIRTS = 'shirts', -} - export function getDemoRows(arg: string | null) { if (arg === DemoRows.CI) { return cloneDeep(ci); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts index 826c49d328f21..5cebae5bb669f 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata/index.ts @@ -8,7 +8,8 @@ import { sortBy } from 'lodash'; import { ExpressionFunctionDefinition } from 'src/plugins/expressions'; // @ts-ignore unconverted lib file import { queryDatatable } from '../../../../common/lib/datatable/query'; -import { DemoRows, getDemoRows } from './get_demo_rows'; +import { DemoRows } from './demo_rows_types'; +import { getDemoRows } from './get_demo_rows'; import { Filter, Datatable, DatatableColumn, DatatableRow } from '../../../../types'; import { getFunctionHelp } from '../../../../i18n'; diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/templates/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/templates/index.ts index eba0b47f7dc13..88d2b904e6cb3 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/templates/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/templates/index.ts @@ -7,7 +7,7 @@ import { applyTemplateStrings } from '../../i18n/templates'; import darkTemplate from './theme_dark.json'; import lightTemplate from './theme_light.json'; -import pitchTemplate from './pitch_presentation.json'; +// import pitchTemplate from './pitch_presentation.json'; import statusTemplate from './status_report.json'; import summaryTemplate from './summary_report.json'; @@ -15,7 +15,7 @@ import summaryTemplate from './summary_report.json'; export const templateSpecs = applyTemplateStrings([ darkTemplate, lightTemplate, - pitchTemplate, + // pitchTemplate, statusTemplate, summaryTemplate, ]); diff --git a/x-pack/legacy/plugins/canvas/i18n/functions/dict/demodata.ts b/x-pack/legacy/plugins/canvas/i18n/functions/dict/demodata.ts index caedbfdec5be4..35a5b86f752dc 100644 --- a/x-pack/legacy/plugins/canvas/i18n/functions/dict/demodata.ts +++ b/x-pack/legacy/plugins/canvas/i18n/functions/dict/demodata.ts @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { demodata } from '../../../canvas_plugin_src/functions/server/demodata'; import { FunctionHelp } from '../function_help'; import { FunctionFactory } from '../../../types'; -import { DemoRows } from '../../../canvas_plugin_src/functions/server/demodata/get_demo_rows'; +import { DemoRows } from '../../../canvas_plugin_src/functions/server/demodata/demo_rows_types'; export const help: FunctionHelp> = { help: i18n.translate('xpack.canvas.functions.demodataHelpText', { diff --git a/x-pack/legacy/plugins/canvas/i18n/templates/template_strings.ts b/x-pack/legacy/plugins/canvas/i18n/templates/template_strings.ts index 5ab6a908641de..d8e4d51706be9 100644 --- a/x-pack/legacy/plugins/canvas/i18n/templates/template_strings.ts +++ b/x-pack/legacy/plugins/canvas/i18n/templates/template_strings.ts @@ -37,14 +37,6 @@ export const getTemplateStrings = (): TemplateStringDict => ({ defaultMessage: 'Light color themed presentation deck', }), }, - Pitch: { - name: i18n.translate('xpack.canvas.templates.pitchName', { - defaultMessage: 'Pitch', - }), - help: i18n.translate('xpack.canvas.templates.pitchHelp', { - defaultMessage: 'Branded presentation with large photos', - }), - }, Status: { name: i18n.translate('xpack.canvas.templates.statusName', { defaultMessage: 'Status', @@ -62,3 +54,14 @@ export const getTemplateStrings = (): TemplateStringDict => ({ }), }, }); + +export const getUnusedTemplateStrings = (): TemplateStringDict => ({ + Pitch: { + name: i18n.translate('xpack.canvas.templates.pitchName', { + defaultMessage: 'Pitch', + }), + help: i18n.translate('xpack.canvas.templates.pitchHelp', { + defaultMessage: 'Branded presentation with large photos', + }), + }, +}); diff --git a/x-pack/legacy/plugins/canvas/tasks/mocks/downloadWorkpad.js b/x-pack/legacy/plugins/canvas/tasks/mocks/downloadWorkpad.js new file mode 100644 index 0000000000000..3571448c11aa9 --- /dev/null +++ b/x-pack/legacy/plugins/canvas/tasks/mocks/downloadWorkpad.js @@ -0,0 +1,13 @@ +/* + * 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 downloadWorkpad = async workpadId => console.log(`Download workpad ${workpadId}`); + +export const downloadRenderedWorkpad = async renderedWorkpad => + console.log(`Download workpad ${renderedWorkpad.id}`); + +export const downloadRuntime = async basePath => console.log(`Download run time at ${basePath}`); + +export const downloadZippedRuntime = async data => console.log(`Downloading data ${data}`); diff --git a/x-pack/legacy/plugins/maps/public/angular/services/gis_map_saved_object_loader.js b/x-pack/legacy/plugins/maps/public/angular/services/gis_map_saved_object_loader.js index 252d602e8f564..bc636c0b200f8 100644 --- a/x-pack/legacy/plugins/maps/public/angular/services/gis_map_saved_object_loader.js +++ b/x-pack/legacy/plugins/maps/public/angular/services/gis_map_saved_object_loader.js @@ -17,6 +17,7 @@ module.service('gisMapSavedObjectLoader', function() { const services = { savedObjectsClient, indexPatterns: npStart.plugins.data.indexPatterns, + search: npStart.plugins.data.search, chrome: npStart.core.chrome, overlays: npStart.core.overlays, }; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/attribution_control/_attribution_control.scss b/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/attribution_control/_attribution_control.scss index 9ebaee57fba4d..e319535b4a45c 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/attribution_control/_attribution_control.scss +++ b/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/attribution_control/_attribution_control.scss @@ -4,3 +4,7 @@ pointer-events: all; padding-left: $euiSizeM; } + +.mapAttributionControl__fullScreen { + margin-left: $euiSizeXXL * 4; +} diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/attribution_control/index.js b/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/attribution_control/index.js index e73a51ffa2ced..8bad536b39245 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/attribution_control/index.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/attribution_control/index.js @@ -7,10 +7,12 @@ import { connect } from 'react-redux'; import { AttributionControl } from './view'; import { getLayerList } from '../../../selectors/map_selectors'; +import { getIsFullScreen } from '../../../selectors/ui_selectors'; function mapStateToProps(state = {}) { return { layerList: getLayerList(state), + isFullScreen: getIsFullScreen(state), }; } diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/attribution_control/view.js b/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/attribution_control/view.js index 161b5b81c1255..8f11d1b23376c 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/attribution_control/view.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/attribution_control/view.js @@ -7,6 +7,7 @@ import React, { Fragment } from 'react'; import _ from 'lodash'; import { EuiText, EuiLink } from '@elastic/eui'; +import classNames from 'classnames'; export class AttributionControl extends React.Component { state = { @@ -86,7 +87,11 @@ export class AttributionControl extends React.Component { return null; } return ( -
+
{this._renderAttributions()} diff --git a/x-pack/legacy/plugins/monitoring/common/constants.ts b/x-pack/legacy/plugins/monitoring/common/constants.ts index 9a4030f3eb214..3a4c7b71dcd03 100644 --- a/x-pack/legacy/plugins/monitoring/common/constants.ts +++ b/x-pack/legacy/plugins/monitoring/common/constants.ts @@ -239,11 +239,15 @@ export const ALERT_TYPE_PREFIX = 'monitoring_'; * This is the alert type id for the license expiration alert */ export const ALERT_TYPE_LICENSE_EXPIRATION = `${ALERT_TYPE_PREFIX}alert_type_license_expiration`; +/** + * This is the alert type id for the cluster state alert + */ +export const ALERT_TYPE_CLUSTER_STATE = `${ALERT_TYPE_PREFIX}alert_type_cluster_state`; /** * A listing of all alert types */ -export const ALERT_TYPES = [ALERT_TYPE_LICENSE_EXPIRATION]; +export const ALERT_TYPES = [ALERT_TYPE_LICENSE_EXPIRATION, ALERT_TYPE_CLUSTER_STATE]; /** * Matches the id for the built-in in email action type @@ -254,7 +258,7 @@ export const ALERT_ACTION_TYPE_EMAIL = '.email'; /** * The number of alerts that have been migrated */ -export const NUMBER_OF_MIGRATED_ALERTS = 1; +export const NUMBER_OF_MIGRATED_ALERTS = 2; /** * The advanced settings config name for the email address diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/alerts.js b/x-pack/legacy/plugins/monitoring/public/components/alerts/alerts.js index 11fcef73a4b97..95c1af5549198 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/alerts/alerts.js +++ b/x-pack/legacy/plugins/monitoring/public/components/alerts/alerts.js @@ -6,10 +6,15 @@ import React from 'react'; import chrome from '../../np_imports/ui/chrome'; -import { capitalize } from 'lodash'; +import { capitalize, get } from 'lodash'; import { formatDateTimeLocal } from '../../../common/formatting'; import { formatTimestampToDuration } from '../../../common'; -import { CALCULATE_DURATION_SINCE, EUI_SORT_DESCENDING } from '../../../common/constants'; +import { + CALCULATE_DURATION_SINCE, + EUI_SORT_DESCENDING, + ALERT_TYPE_LICENSE_EXPIRATION, + ALERT_TYPE_CLUSTER_STATE, +} from '../../../common/constants'; import { mapSeverity } from './map_severity'; import { FormattedAlert } from 'plugins/monitoring/components/alerts/formatted_alert'; import { EuiMonitoringTable } from 'plugins/monitoring/components/table'; @@ -21,6 +26,8 @@ const linkToCategories = { 'elasticsearch/indices': 'Elasticsearch Indices', 'kibana/instances': 'Kibana Instances', 'logstash/instances': 'Logstash Nodes', + [ALERT_TYPE_LICENSE_EXPIRATION]: 'License expiration', + [ALERT_TYPE_CLUSTER_STATE]: 'Cluster state', }; const getColumns = (kbnUrl, scope, timezone) => [ { @@ -94,19 +101,22 @@ const getColumns = (kbnUrl, scope, timezone) => [ }), field: 'message', sortable: true, - render: (message, alert) => ( - { - scope.$evalAsync(() => { - kbnUrl.changePath(target); - }); - }} - /> - ), + render: (_message, alert) => { + const message = get(alert, 'message.text', get(alert, 'message', '')); + return ( + { + scope.$evalAsync(() => { + kbnUrl.changePath(target); + }); + }} + /> + ); + }, }, { name: i18n.translate('xpack.monitoring.alerts.categoryColumnTitle', { @@ -148,8 +158,8 @@ const getColumns = (kbnUrl, scope, timezone) => [ export const Alerts = ({ alerts, angular, sorting, pagination, onTableChange }) => { const alertsFlattened = alerts.map(alert => ({ ...alert, - status: alert.metadata.severity, - category: alert.metadata.link, + status: get(alert, 'metadata.severity', get(alert, 'severity', 0)), + category: get(alert, 'metadata.link', get(alert, 'type', null)), })); const injector = chrome.dangerouslyGetActiveInjector(); diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/__snapshots__/step1.test.tsx.snap b/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/__snapshots__/step1.test.tsx.snap index 94d951a94fe29..cb1081c0c14da 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/__snapshots__/step1.test.tsx.snap +++ b/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/__snapshots__/step1.test.tsx.snap @@ -25,6 +25,7 @@ exports[`Step1 editing should allow for editing 1`] = ` "actionTypeId": "1abc", "config": Object {}, "id": "1", + "isPreconfigured": false, "name": "Testing", } } diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/configuration.tsx b/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/configuration.tsx index 0933cd22db7c9..eaa474ba177b1 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/configuration.tsx +++ b/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/configuration.tsx @@ -61,7 +61,7 @@ export const AlertsConfiguration: React.FC = ( async function fetchEmailActions() { const kibanaActions = await kfetch({ method: 'GET', - pathname: `/api/action/_find`, + pathname: `/api/action/_getAll`, }); const actions = kibanaActions.data.filter( diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/step1.test.tsx b/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/step1.test.tsx index 650294c29e9a5..19a1a61d00a42 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/step1.test.tsx +++ b/x-pack/legacy/plugins/monitoring/public/components/alerts/configuration/step1.test.tsx @@ -27,6 +27,7 @@ describe('Step1', () => { actionTypeId: '1abc', name: 'Testing', config: {}, + isPreconfigured: false, }, ]; const selectedEmailActionId = emailActions[0].id; @@ -83,6 +84,7 @@ describe('Step1', () => { actionTypeId: '.email', name: '', config: {}, + isPreconfigured: false, }, ], selectedEmailActionId: NEW_ACTION_ID, diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/status.test.tsx b/x-pack/legacy/plugins/monitoring/public/components/alerts/status.test.tsx index 258a5b68db372..d3cf4b463a2cc 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/alerts/status.test.tsx +++ b/x-pack/legacy/plugins/monitoring/public/components/alerts/status.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { kfetch } from 'ui/kfetch'; import { AlertsStatus, AlertsStatusProps } from './status'; -import { ALERT_TYPE_PREFIX } from '../../../common/constants'; +import { ALERT_TYPES } from '../../../common/constants'; import { getSetupModeState } from '../../lib/setup_mode'; import { mockUseEffects } from '../../jest.helpers'; @@ -63,11 +63,7 @@ describe('Status', () => { it('should render a success message if all alerts have been migrated and in setup mode', async () => { (kfetch as jest.Mock).mockReturnValue({ - data: [ - { - alertTypeId: ALERT_TYPE_PREFIX, - }, - ], + data: ALERT_TYPES.map(type => ({ alertTypeId: type })), }); (getSetupModeState as jest.Mock).mockReturnValue({ diff --git a/x-pack/legacy/plugins/monitoring/public/components/alerts/status.tsx b/x-pack/legacy/plugins/monitoring/public/components/alerts/status.tsx index 072a98b123452..5f5329bf7fff8 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/alerts/status.tsx +++ b/x-pack/legacy/plugins/monitoring/public/components/alerts/status.tsx @@ -142,7 +142,7 @@ export const AlertsStatus: React.FC = (props: AlertsStatusPro ); } - const allMigrated = kibanaAlerts.length === NUMBER_OF_MIGRATED_ALERTS; + const allMigrated = kibanaAlerts.length >= NUMBER_OF_MIGRATED_ALERTS; if (allMigrated) { if (setupModeEnabled) { return ( diff --git a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/alerts_panel.js b/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/alerts_panel.js index 8455fb8cf3088..d87ff98e79be0 100644 --- a/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/alerts_panel.js +++ b/x-pack/legacy/plugins/monitoring/public/components/cluster/overview/alerts_panel.js @@ -6,14 +6,12 @@ import React, { Fragment } from 'react'; import moment from 'moment-timezone'; -import chrome from '../../../np_imports/ui/chrome'; import { FormattedAlert } from 'plugins/monitoring/components/alerts/formatted_alert'; import { mapSeverity } from 'plugins/monitoring/components/alerts/map_severity'; import { formatTimestampToDuration } from '../../../../common/format_timestamp_to_duration'; import { CALCULATE_DURATION_SINCE, KIBANA_ALERTING_ENABLED, - ALERT_TYPE_LICENSE_EXPIRATION, CALCULATE_DURATION_UNTIL, } from '../../../../common/constants'; import { formatDateTimeLocal } from '../../../../common/formatting'; @@ -31,6 +29,37 @@ import { EuiLink, } from '@elastic/eui'; +function replaceTokens(alert) { + if (!alert.message.tokens) { + return alert.message.text; + } + + let text = alert.message.text; + + for (const token of alert.message.tokens) { + if (token.type === 'time') { + text = text.replace( + token.startToken, + token.isRelative + ? formatTimestampToDuration(alert.expirationTime, CALCULATE_DURATION_UNTIL) + : moment.tz(alert.expirationTime, moment.tz.guess()).format('LLL z') + ); + } else if (token.type === 'link') { + const linkPart = new RegExp(`${token.startToken}(.+?)${token.endToken}`).exec(text); + // TODO: we assume this is at the end, which works for now but will not always work + const nonLinkText = text.replace(linkPart[0], ''); + text = ( + + {nonLinkText} + {linkPart[1]} + + ); + } + } + + return text; +} + export function AlertsPanel({ alerts, changeUrl }) { const goToAlerts = () => changeUrl('/alerts'); @@ -58,9 +87,6 @@ export function AlertsPanel({ alerts, changeUrl }) { severityIcon.iconType = 'check'; } - const injector = chrome.dangerouslyGetActiveInjector(); - const timezone = injector.get('config').get('dateFormat:tz'); - return ( @@ -96,14 +122,7 @@ export function AlertsPanel({ alerts, changeUrl }) { const alertsList = KIBANA_ALERTING_ENABLED ? alerts.map((alert, idx) => { const callOutProps = mapSeverity(alert.severity); - let message = alert.message - // scan message prefix and replace relative times - // \w: Matches any alphanumeric character from the basic Latin alphabet, including the underscore. Equivalent to [A-Za-z0-9_]. - .replace( - '#relative', - formatTimestampToDuration(alert.expirationTime, CALCULATE_DURATION_UNTIL) - ) - .replace('#absolute', moment.tz(alert.expirationTime, moment.tz.guess()).format('LLL z')); + const message = replaceTokens(alert); if (!alert.isFiring) { callOutProps.title = i18n.translate( @@ -118,22 +137,30 @@ export function AlertsPanel({ alerts, changeUrl }) { ); callOutProps.color = 'success'; callOutProps.iconType = 'check'; - } else { - if (alert.type === ALERT_TYPE_LICENSE_EXPIRATION) { - message = ( - - {message} -   - Please update your license - - ); - } } return ( - -

{message}

-
+ + +

{message}

+ +

+ +

+
+
+ +
); }) : alerts.map((item, index) => ( diff --git a/x-pack/legacy/plugins/monitoring/public/views/alerts/index.js b/x-pack/legacy/plugins/monitoring/public/views/alerts/index.js index 7c065a78a8af9..62cc985887e9f 100644 --- a/x-pack/legacy/plugins/monitoring/public/views/alerts/index.js +++ b/x-pack/legacy/plugins/monitoring/public/views/alerts/index.js @@ -18,25 +18,37 @@ import { Alerts } from '../../components/alerts'; import { MonitoringViewBaseEuiTableController } from '../base_eui_table_controller'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiPage, EuiPageBody, EuiPageContent, EuiSpacer, EuiLink } from '@elastic/eui'; -import { CODE_PATH_ALERTS } from '../../../common/constants'; +import { CODE_PATH_ALERTS, KIBANA_ALERTING_ENABLED } from '../../../common/constants'; function getPageData($injector) { const globalState = $injector.get('globalState'); const $http = $injector.get('$http'); const Private = $injector.get('Private'); - const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/legacy_alerts`; + const url = KIBANA_ALERTING_ENABLED + ? `../api/monitoring/v1/alert_status` + : `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/legacy_alerts`; const timeBounds = timefilter.getBounds(); + const data = { + timeRange: { + min: timeBounds.min.toISOString(), + max: timeBounds.max.toISOString(), + }, + }; + + if (!KIBANA_ALERTING_ENABLED) { + data.ccs = globalState.ccs; + } return $http - .post(url, { - ccs: globalState.ccs, - timeRange: { - min: timeBounds.min.toISOString(), - max: timeBounds.max.toISOString(), - }, + .post(url, data) + .then(response => { + const result = get(response, 'data', []); + if (KIBANA_ALERTING_ENABLED) { + return result.alerts; + } + return result; }) - .then(response => get(response, 'data', [])) .catch(err => { const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider); return ajaxErrorHandlers(err); diff --git a/x-pack/legacy/plugins/rollup/kibana.json b/x-pack/legacy/plugins/rollup/kibana.json index 3781d59d8c0f3..3df8bd7c187d5 100644 --- a/x-pack/legacy/plugins/rollup/kibana.json +++ b/x-pack/legacy/plugins/rollup/kibana.json @@ -4,7 +4,8 @@ "requiredPlugins": [ "home", "index_management", - "metrics" + "metrics", + "indexPatternManagement" ], "optionalPlugins": [ "usageCollection" diff --git a/x-pack/legacy/plugins/rollup/public/index_pattern_creation/rollup_index_pattern_creation_config.js b/x-pack/legacy/plugins/rollup/public/index_pattern_creation/rollup_index_pattern_creation_config.js index f0eb21a219442..f4de2a3098127 100644 --- a/x-pack/legacy/plugins/rollup/public/index_pattern_creation/rollup_index_pattern_creation_config.js +++ b/x-pack/legacy/plugins/rollup/public/index_pattern_creation/rollup_index_pattern_creation_config.js @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { RollupPrompt } from './components/rollup_prompt'; -import { IndexPatternCreationConfig } from '../../../../../../src/legacy/core_plugins/management/public'; +import { IndexPatternCreationConfig } from '../../../../../../src/plugins/index_pattern_management/public'; const rollupIndexPatternTypeName = i18n.translate( 'xpack.rollupJobs.editRollupIndexPattern.createIndex.defaultTypeName', diff --git a/x-pack/legacy/plugins/rollup/public/index_pattern_list/rollup_index_pattern_list_config.js b/x-pack/legacy/plugins/rollup/public/index_pattern_list/rollup_index_pattern_list_config.js index fbf2612b83aa8..809a76d1868b2 100644 --- a/x-pack/legacy/plugins/rollup/public/index_pattern_list/rollup_index_pattern_list_config.js +++ b/x-pack/legacy/plugins/rollup/public/index_pattern_list/rollup_index_pattern_list_config.js @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { IndexPatternListConfig } from '../../../../../../src/legacy/core_plugins/management/public'; +import { IndexPatternListConfig } from '../../../../../../src/plugins/index_pattern_management/public'; function isRollup(indexPattern) { return ( diff --git a/x-pack/legacy/plugins/rollup/public/legacy.ts b/x-pack/legacy/plugins/rollup/public/legacy.ts index ec530e63408f4..83945110c2c76 100644 --- a/x-pack/legacy/plugins/rollup/public/legacy.ts +++ b/x-pack/legacy/plugins/rollup/public/legacy.ts @@ -6,14 +6,8 @@ import { npSetup, npStart } from 'ui/new_platform'; import { RollupPlugin } from './plugin'; -import { setup as management } from '../../../../../src/legacy/core_plugins/management/public/legacy'; const plugin = new RollupPlugin(); -export const setup = plugin.setup(npSetup.core, { - ...npSetup.plugins, - __LEGACY: { - managementLegacy: management, - }, -}); +export const setup = plugin.setup(npSetup.core, npSetup.plugins); export const start = plugin.start(npStart.core, npStart.plugins); diff --git a/x-pack/legacy/plugins/rollup/public/plugin.ts b/x-pack/legacy/plugins/rollup/public/plugin.ts index c58975419e20f..5782e88c3448b 100644 --- a/x-pack/legacy/plugins/rollup/public/plugin.ts +++ b/x-pack/legacy/plugins/rollup/public/plugin.ts @@ -7,7 +7,6 @@ import { i18n } from '@kbn/i18n'; import { CoreSetup, CoreStart, Plugin } from 'kibana/public'; import { PluginsStart } from './legacy_imports'; -import { ManagementSetup as ManagementSetupLegacy } from '../../../../../src/legacy/core_plugins/management/public/np_ready'; import { rollupBadgeExtension, rollupToggleExtension } from './extend_index_management'; // @ts-ignore import { RollupIndexPatternCreationConfig } from './index_pattern_creation/rollup_index_pattern_creation_config'; @@ -26,6 +25,7 @@ import { import { CRUD_APP_BASE_PATH } from './crud_app/constants'; import { ManagementSetup } from '../../../../../src/plugins/management/public'; import { IndexMgmtSetup } from '../../../../plugins/index_management/public'; +import { IndexPatternManagementSetup } from '../../../../../src/plugins/index_pattern_management/public'; import { search } from '../../../../../src/plugins/data/public'; // @ts-ignore import { setEsBaseAndXPackBase, setHttp } from './crud_app/services'; @@ -33,23 +33,16 @@ import { setNotifications, setFatalErrors } from './kibana_services'; import { renderApp } from './application'; export interface RollupPluginSetupDependencies { - __LEGACY: { - managementLegacy: ManagementSetupLegacy; - }; home?: HomePublicPluginSetup; management: ManagementSetup; indexManagement?: IndexMgmtSetup; + indexPatternManagement: IndexPatternManagementSetup; } export class RollupPlugin implements Plugin { setup( core: CoreSetup, - { - __LEGACY: { managementLegacy }, - home, - management, - indexManagement, - }: RollupPluginSetupDependencies + { home, management, indexManagement, indexPatternManagement }: RollupPluginSetupDependencies ) { setFatalErrors(core.fatalErrors); @@ -61,8 +54,8 @@ export class RollupPlugin implements Plugin { const isRollupIndexPatternsEnabled = core.uiSettings.get(CONFIG_ROLLUPS); if (isRollupIndexPatternsEnabled) { - managementLegacy.indexPattern.creation.add(RollupIndexPatternCreationConfig); - managementLegacy.indexPattern.list.add(RollupIndexPatternListConfig); + indexPatternManagement.creation.addCreationConfig(RollupIndexPatternCreationConfig); + indexPatternManagement.list.addListConfig(RollupIndexPatternListConfig); } if (home) { diff --git a/x-pack/legacy/plugins/siem/common/constants.ts b/x-pack/legacy/plugins/siem/common/constants.ts index 662fb8fb8ef68..22f1b3beffa35 100644 --- a/x-pack/legacy/plugins/siem/common/constants.ts +++ b/x-pack/legacy/plugins/siem/common/constants.ts @@ -65,8 +65,6 @@ export const INTERNAL_IDENTIFIER = '__internal'; export const INTERNAL_RULE_ID_KEY = `${INTERNAL_IDENTIFIER}_rule_id`; export const INTERNAL_RULE_ALERT_ID_KEY = `${INTERNAL_IDENTIFIER}_rule_alert_id`; export const INTERNAL_IMMUTABLE_KEY = `${INTERNAL_IDENTIFIER}_immutable`; -export const INTERNAL_NOTIFICATION_ID_KEY = `${INTERNAL_IDENTIFIER}_notification_id`; -export const INTERNAL_NOTIFICATION_RULE_ID_KEY = `${INTERNAL_IDENTIFIER}_notification_rule_id`; /** * Detection engine routes diff --git a/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts index 1559285d508ed..2d2db9e70255b 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts @@ -32,7 +32,7 @@ describe('Signal detection rules', () => { esArchiverUnload('prebuilt_rules_loaded'); }); - it('Sorts by activated rules', () => { + it.skip('Sorts by activated rules', () => { loginAndWaitForPageWithoutDateRange(DETECTIONS); goToManageSignalDetectionRules(); waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded(); diff --git a/x-pack/legacy/plugins/siem/public/containers/case/configure/api.ts b/x-pack/legacy/plugins/siem/public/containers/case/configure/api.ts index ed47cdc62a1b6..c24081c777a96 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/configure/api.ts +++ b/x-pack/legacy/plugins/siem/public/containers/case/configure/api.ts @@ -6,7 +6,7 @@ import { isEmpty } from 'lodash/fp'; import { - CasesConnectorsFindResult, + Connector, CasesConfigurePatch, CasesConfigureResponse, CasesConfigureRequest, @@ -18,7 +18,7 @@ import { ApiProps } from '../types'; import { convertToCamelCase, decodeCaseConfigureResponse } from '../utils'; import { CaseConfigure } from './types'; -export const fetchConnectors = async ({ signal }: ApiProps): Promise => { +export const fetchConnectors = async ({ signal }: ApiProps): Promise => { const response = await KibanaServices.get().http.fetch( `${CASES_CONFIGURE_URL}/connectors/_find`, { diff --git a/x-pack/legacy/plugins/siem/public/containers/case/configure/use_connectors.tsx b/x-pack/legacy/plugins/siem/public/containers/case/configure/use_connectors.tsx index d31dcdbee2a14..30108ecf33874 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/configure/use_connectors.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/case/configure/use_connectors.tsx @@ -31,7 +31,7 @@ export const useConnectors = (): ReturnConnectors => { const res = await fetchConnectors({ signal: abortCtrl.signal }); if (!didCancel) { setLoading(false); - setConnectors(res.data); + setConnectors(res); } } catch (error) { if (!didCancel) { diff --git a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts index bc559c5ac4972..f89d21ef1aeb1 100644 --- a/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts +++ b/x-pack/legacy/plugins/siem/public/containers/detection_engine/rules/types.ts @@ -70,7 +70,7 @@ const MetaRule = t.intersection([ }), t.partial({ throttle: t.string, - kibanaSiemAppUrl: t.string, + kibana_siem_app_url: t.string, }), ]); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/__mock__/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/__mock__/index.tsx index a3df3664398ad..135f0f2a7e26d 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/__mock__/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/__mock__/index.tsx @@ -21,6 +21,7 @@ export const connectors: Connector[] = [ id: '123', actionTypeId: '.servicenow', name: 'My Connector', + isPreconfigured: false, config: { apiUrl: 'https://instance1.service-now.com', casesConfiguration: { @@ -48,6 +49,7 @@ export const connectors: Connector[] = [ id: '456', actionTypeId: '.servicenow', name: 'My Connector 2', + isPreconfigured: false, config: { apiUrl: 'https://instance2.service-now.com', casesConfiguration: { diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.test.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.test.ts index efb601b6bd207..8d793f39afa99 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.test.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.test.ts @@ -515,7 +515,7 @@ describe('helpers', () => { actions: [], enabled: false, meta: { - kibanaSiemAppUrl: 'http://localhost:5601/app/siem', + kibana_siem_app_url: 'http://localhost:5601/app/siem', }, throttle: 'no_actions', }; @@ -533,7 +533,7 @@ describe('helpers', () => { actions: [], enabled: false, meta: { - kibanaSiemAppUrl: mockStepData.kibanaSiemAppUrl, + kibana_siem_app_url: mockStepData.kibanaSiemAppUrl, }, throttle: 'no_actions', }; @@ -566,7 +566,7 @@ describe('helpers', () => { ], enabled: false, meta: { - kibanaSiemAppUrl: mockStepData.kibanaSiemAppUrl, + kibana_siem_app_url: mockStepData.kibanaSiemAppUrl, }, throttle: 'rule', }; @@ -599,7 +599,7 @@ describe('helpers', () => { ], enabled: false, meta: { - kibanaSiemAppUrl: mockStepData.kibanaSiemAppUrl, + kibana_siem_app_url: mockStepData.kibanaSiemAppUrl, }, throttle: mockStepData.throttle, }; @@ -631,7 +631,7 @@ describe('helpers', () => { ], enabled: false, meta: { - kibanaSiemAppUrl: mockStepData.kibanaSiemAppUrl, + kibana_siem_app_url: mockStepData.kibanaSiemAppUrl, }, throttle: 'no_actions', }; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts index 151e3a9bdf4d6..7ad116c313361 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/create/helpers.ts @@ -155,7 +155,7 @@ export const formatActionsStepData = (actionsStepData: ActionsStepRule): Actions enabled, throttle: actions.length ? throttle : NOTIFICATION_THROTTLE_NO_ACTIONS, meta: { - kibanaSiemAppUrl, + kibana_siem_app_url: kibanaSiemAppUrl, }, }; }; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx index db1f2298b5ea7..58a1b0fd3133e 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/helpers.tsx @@ -64,7 +64,7 @@ export const getActionsStepsData = ( actions: actions?.map(transformRuleToAlertAction), isNew: false, throttle, - kibanaSiemAppUrl: meta?.kibanaSiemAppUrl, + kibanaSiemAppUrl: meta?.kibana_siem_app_url, enabled, }; }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/add_tags.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/add_tags.ts index 6955e57d099be..14b2e1ae9e366 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/add_tags.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/add_tags.ts @@ -6,5 +6,5 @@ import { INTERNAL_RULE_ALERT_ID_KEY } from '../../../../common/constants'; -export const addTags = (tags: string[] = [], ruleAlertId: string): string[] => +export const addTags = (tags: string[], ruleAlertId: string): string[] => Array.from(new Set([...tags, `${INTERNAL_RULE_ALERT_ID_KEY}:${ruleAlertId}`])); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/build_signals_query.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/build_signals_query.test.ts index 189c596a77125..c6923283bec16 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/build_signals_query.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/build_signals_query.test.ts @@ -41,6 +41,7 @@ describe('buildSignalsSearchQuery', () => { '@timestamp': { gt: from, lte: to, + format: 'epoch_millis', }, }, }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/build_signals_query.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/build_signals_query.ts index b973d4c5f4e98..be0943c014225 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/build_signals_query.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/build_signals_query.ts @@ -32,6 +32,7 @@ export const buildSignalsSearchQuery = ({ ruleId, index, from, to }: BuildSignal '@timestamp': { gt: from, lte: to, + format: 'epoch_millis', }, }, }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/create_notifications.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/create_notifications.test.ts index 073251b68f414..3878f5dae8889 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/create_notifications.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/create_notifications.test.ts @@ -24,7 +24,6 @@ describe('createNotifications', () => { enabled: true, interval: '', name: '', - tags: [], }); expect(alertsClient.create).toHaveBeenCalledWith( @@ -52,7 +51,6 @@ describe('createNotifications', () => { enabled: true, interval: '', name: '', - tags: [], }); expect(alertsClient.create).toHaveBeenCalledWith( diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/create_notifications.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/create_notifications.ts index 3a1697f1c8afc..ccd7576255d83 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/create_notifications.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/create_notifications.ts @@ -17,12 +17,11 @@ export const createNotifications = async ({ ruleAlertId, interval, name, - tags, }: CreateNotificationParams): Promise => alertsClient.create({ data: { name, - tags: addTags(tags, ruleAlertId), + tags: addTags([], ruleAlertId), alertTypeId: NOTIFICATIONS_ID, consumer: APP_ID, params: { @@ -30,7 +29,7 @@ export const createNotifications = async ({ }, schedule: { interval }, enabled, - actions: actions?.map(transformRuleToAlertAction), + actions: actions.map(transformRuleToAlertAction), throttle: null, }, }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/rules_notification_alert_type.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/rules_notification_alert_type.ts index 546488caa5ee7..e4ad53de742d6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/rules_notification_alert_type.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/rules_notification_alert_type.ts @@ -45,7 +45,9 @@ export const rulesNotificationAlertType = ({ const ruleParams = { ...ruleAlertParams, name: ruleName, id: ruleAlertSavedObject.id }; const fromInMs = parseScheduleDates( - previousStartedAt ? previousStartedAt.toISOString() : `now-${ruleParams.interval}` + previousStartedAt + ? previousStartedAt.toISOString() + : `now-${ruleAlertSavedObject.attributes.schedule.interval}` )?.format('x'); const toInMs = parseScheduleDates(startedAt.toISOString())?.format('x'); @@ -53,7 +55,7 @@ export const rulesNotificationAlertType = ({ from: fromInMs, to: toInMs, index: ruleParams.outputIndex, - ruleId: ruleParams.ruleId!, + ruleId: ruleParams.ruleId, callCluster: services.callCluster, }); @@ -61,14 +63,14 @@ export const rulesNotificationAlertType = ({ from: fromInMs, to: toInMs, id: ruleAlertSavedObject.id, - kibanaSiemAppUrl: ruleAlertParams.meta?.kibanaSiemAppUrl as string, + kibanaSiemAppUrl: ruleAlertParams.meta?.kibana_siem_app_url, }); logger.info( `Found ${signalsCount} signals using signal rule name: "${ruleParams.name}", id: "${params.ruleAlertId}", rule_id: "${ruleParams.ruleId}" in "${ruleParams.outputIndex}" index` ); - if (signalsCount) { + if (signalsCount !== 0) { const alertInstance = services.alertInstanceFactory(alertId); scheduleNotificationActions({ alertInstance, signalsCount, resultsLink, ruleParams }); } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/schedule_notification_actions.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/schedule_notification_actions.ts index 749b892ef506f..9f145af79ca90 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/schedule_notification_actions.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/schedule_notification_actions.ts @@ -8,7 +8,7 @@ import { mapKeys, snakeCase } from 'lodash/fp'; import { AlertInstance } from '../../../../../../../plugins/alerting/server'; import { RuleTypeParams } from '../types'; -type NotificationRuleTypeParams = RuleTypeParams & { +export type NotificationRuleTypeParams = RuleTypeParams & { name: string; id: string; }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/types.ts index 128a7965cd7dc..32a8737adc7c9 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/types.ts @@ -45,10 +45,11 @@ export interface Clients { alertsClient: AlertsClient; } -export type UpdateNotificationParams = Omit & { +export type UpdateNotificationParams = Omit< + NotificationAlertParams, + 'interval' | 'actions' | 'tags' +> & { actions: RuleAlertAction[]; - id?: string; - tags?: string[]; interval: string | null | undefined; ruleAlertId: string; } & Clients; @@ -64,8 +65,6 @@ export interface NotificationAlertParams { ruleAlertId: string; interval: string; name: string; - tags?: string[]; - throttle?: null; } export type CreateNotificationParams = NotificationAlertParams & Clients; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/update_notifications.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/update_notifications.test.ts index 4c077dd9fc1fb..e1f7526438c31 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/update_notifications.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/update_notifications.test.ts @@ -9,6 +9,7 @@ import { updateNotifications } from './update_notifications'; import { readNotifications } from './read_notifications'; import { createNotifications } from './create_notifications'; import { getNotificationResult } from '../routes/__mocks__/request_responses'; +import { UpdateNotificationParams } from './types'; jest.mock('./read_notifications'); jest.mock('./create_notifications'); @@ -30,7 +31,6 @@ describe('updateNotifications', () => { enabled: true, interval: '10m', name: '', - tags: [], }); expect(alertsClient.update).toHaveBeenCalledWith( @@ -48,14 +48,13 @@ describe('updateNotifications', () => { it('should create a new notification if did not exist', async () => { (readNotifications as jest.Mock).mockResolvedValue(null); - const params = { + const params: UpdateNotificationParams = { alertsClient, actions: [], ruleAlertId: 'new-rule-id', enabled: true, interval: '10m', name: '', - tags: [], }; await updateNotifications(params); @@ -73,7 +72,6 @@ describe('updateNotifications', () => { enabled: true, interval: null, name: '', - tags: [], }); expect(alertsClient.delete).toHaveBeenCalledWith( @@ -98,7 +96,6 @@ describe('updateNotifications', () => { enabled: true, interval: '10m', name: '', - tags: [], }); expect(alertsClient.update).toHaveBeenCalledWith( @@ -125,10 +122,8 @@ describe('updateNotifications', () => { alertsClient, actions: [], enabled: true, - id: notification.id, ruleAlertId, name: notification.name, - tags: notification.tags, interval: null, }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/update_notifications.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/update_notifications.ts index 3197d21c0e95a..ac0de406aceb2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/update_notifications.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/update_notifications.ts @@ -15,50 +15,41 @@ export const updateNotifications = async ({ alertsClient, actions, enabled, - id, ruleAlertId, name, - tags, interval, }: UpdateNotificationParams): Promise => { - const notification = await readNotifications({ alertsClient, id, ruleAlertId }); + const notification = await readNotifications({ alertsClient, id: undefined, ruleAlertId }); if (interval && notification) { - const result = await alertsClient.update({ + return alertsClient.update({ id: notification.id, data: { - tags: addTags(tags, ruleAlertId), + tags: addTags([], ruleAlertId), name, schedule: { interval, }, - actions: actions?.map(transformRuleToAlertAction), + actions: actions.map(transformRuleToAlertAction), params: { ruleAlertId, }, throttle: null, }, }); - return result; - } - - if (interval && !notification) { - const result = await createNotifications({ + } else if (interval && !notification) { + return createNotifications({ alertsClient, enabled, - tags, name, interval, actions, ruleAlertId, }); - return result; - } - - if (!interval && notification) { + } else if (!interval && notification) { await alertsClient.delete({ id: notification.id }); return null; + } else { + return null; } - - return null; }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/utils.ts index 5dc7e7fc30b7f..c91c4490e8eba 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/utils.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/utils.ts @@ -10,7 +10,7 @@ export const getNotificationResultsLink = ({ from, to, }: { - kibanaSiemAppUrl: string; + kibanaSiemAppUrl?: string; id: string; from?: string; to?: string; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts index 3b24b722f7356..e400360a5a5b2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts @@ -383,6 +383,7 @@ export const createActionResult = (): ActionResult => ({ actionTypeId: 'action-id-1', name: '', config: {}, + isPreconfigured: false, }); export const nonRuleAlert = () => ({ @@ -450,25 +451,31 @@ export const getResult = (): RuleAlertType => ({ lists: [ { field: 'source.ip', - boolean_operator: 'and', - values: [ - { - name: '127.0.0.1', - type: 'value', - }, - ], + values_operator: 'included', + values_type: 'exists', }, { field: 'host.name', - boolean_operator: 'and not', + values_operator: 'excluded', + values_type: 'match', values: [ { name: 'rock01', - type: 'value', }, + ], + and: [ { - name: 'mothra', - type: 'value', + field: 'host.id', + values_operator: 'included', + values_type: 'match_all', + values: [ + { + name: '123', + }, + { + name: '678', + }, + ], }, ], }, @@ -512,6 +519,7 @@ export const updateActionResult = (): ActionResult => ({ actionTypeId: 'action-id-1', name: '', config: {}, + isPreconfigured: false, }); export const getMockPrivilegesResult = () => ({ diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/utils.ts index 63fe51f2c81cd..c929b0718207d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/utils.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/utils.ts @@ -141,25 +141,31 @@ export const getOutputRuleAlertForRest = (): Omit< lists: [ { field: 'source.ip', - boolean_operator: 'and', - values: [ - { - name: '127.0.0.1', - type: 'value', - }, - ], + values_operator: 'included', + values_type: 'exists', }, { field: 'host.name', - boolean_operator: 'and not', + values_operator: 'excluded', + values_type: 'match', values: [ { name: 'rock01', - type: 'value', }, + ], + and: [ { - name: 'mothra', - type: 'value', + field: 'host.id', + values_operator: 'included', + values_type: 'match_all', + values: [ + { + name: '123', + }, + { + name: '678', + }, + ], }, ], }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts index d0e36515946a8..5377e9039785e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts @@ -144,6 +144,7 @@ export const createRulesBulkRoute = (router: IRouter) => { note, version, lists, + actions: throttle === 'rule' ? actions : [], // Only enable actions if throttle is set to rule, otherwise we are a notification and should not enable it, }); const ruleActions = await updateRulesNotifications({ diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts index 6038ad2095323..9a329b78b8f12 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/create_rules_route.ts @@ -132,6 +132,7 @@ export const createRulesRoute = (router: IRouter): void => { note, version: 1, lists, + actions: throttle === 'rule' ? actions : [], // Only enable actions if throttle is rule, otherwise we are a notification and should not enable it, }); const ruleActions = await updateRulesNotifications({ diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts index 43e970702ba72..29ae5056a3ae8 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/import_rules_route.ts @@ -196,6 +196,7 @@ export const importRulesRoute = (router: IRouter, config: LegacyServices['config note, version, lists, + actions: [], // Actions are not imported nor exported at this time }); resolve({ rule_id: ruleId, status_code: 200 }); } else if (rule != null && request.query.overwrite) { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts index 85255594ee480..8c0fceb7a5f29 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts @@ -121,15 +121,15 @@ export const patchRulesBulkRoute = (router: IRouter) => { anomalyThreshold, machineLearningJobId, }); - if (rule != null) { + if (rule != null && rule.enabled != null && rule.name != null) { const ruleActions = await updateRulesNotifications({ ruleAlertId: rule.id, alertsClient, savedObjectsClient, - enabled: rule.enabled!, + enabled: rule.enabled, actions, throttle, - name: rule.name!, + name: rule.name, }); const ruleStatuses = await savedObjectsClient.find< IRuleSavedAttributesSavedObjectAttributes diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts index f553ccd2c6f81..9c5000d70e5fe 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_route.ts @@ -117,15 +117,15 @@ export const patchRulesRoute = (router: IRouter) => { anomalyThreshold, machineLearningJobId, }); - if (rule != null) { + if (rule != null && rule.enabled != null && rule.name != null) { const ruleActions = await updateRulesNotifications({ ruleAlertId: rule.id, alertsClient, savedObjectsClient, - enabled: rule.enabled!, + enabled: rule.enabled, actions, throttle, - name: rule.name!, + name: rule.name, }); const ruleStatuses = await savedObjectsClient.find< IRuleSavedAttributesSavedObjectAttributes diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts index 9916972f41843..36e15780f5cb3 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts @@ -122,6 +122,7 @@ export const updateRulesBulkRoute = (router: IRouter) => { note, version, lists, + actions, }); if (rule != null) { const ruleActions = await updateRulesNotifications({ diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts index 21dd2a4429cca..0444c757a9b31 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/update_rules_route.ts @@ -118,6 +118,7 @@ export const updateRulesRoute = (router: IRouter) => { note, version, lists, + actions: throttle === 'rule' ? actions : [], // Only enable actions if throttle is rule, otherwise we are a notification and should not enable it }); if (rule != null) { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/validate.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/validate.test.ts index 77e05796fbcbe..7537401e5a366 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/validate.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/validate.test.ts @@ -74,25 +74,31 @@ export const ruleOutput: RulesSchema = { lists: [ { field: 'source.ip', - boolean_operator: 'and', - values: [ - { - name: '127.0.0.1', - type: 'value', - }, - ], + values_operator: 'included', + values_type: 'exists', }, { field: 'host.name', - boolean_operator: 'and not', + values_operator: 'excluded', + values_type: 'match', values: [ { name: 'rock01', - type: 'value', }, + ], + and: [ { - name: 'mothra', - type: 'value', + field: 'host.id', + values_operator: 'included', + values_type: 'match_all', + values: [ + { + name: '123', + }, + { + name: '678', + }, + ], }, ], }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts index b10627d151fa2..8c741c937bf15 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/add_prepackaged_rules_schema.test.ts @@ -1561,25 +1561,31 @@ describe('add prepackaged rules schema', () => { lists: [ { field: 'source.ip', - boolean_operator: 'and', - values: [ - { - name: '127.0.0.1', - type: 'value', - }, - ], + values_operator: 'included', + values_type: 'exists', }, { field: 'host.name', - boolean_operator: 'and not', + values_operator: 'excluded', + values_type: 'match', values: [ { name: 'rock01', - type: 'value', }, + ], + and: [ { - name: 'mothra', - type: 'value', + field: 'host.id', + values_operator: 'included', + values_type: 'match_all', + values: [ + { + name: '123', + }, + { + name: '678', + }, + ], }, ], }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts index 08bd01ee9a1a0..e56e8e5fe34d3 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/create_rules_schema.test.ts @@ -1526,25 +1526,31 @@ describe('create rules schema', () => { lists: [ { field: 'source.ip', - boolean_operator: 'and', - values: [ - { - name: '127.0.0.1', - type: 'value', - }, - ], + values_operator: 'included', + values_type: 'exists', }, { field: 'host.name', - boolean_operator: 'and not', + values_operator: 'excluded', + values_type: 'match', values: [ { name: 'rock01', - type: 'value', }, + ], + and: [ { - name: 'mothra', - type: 'value', + field: 'host.id', + values_operator: 'included', + values_type: 'match_all', + values: [ + { + name: '123', + }, + { + name: '678', + }, + ], }, ], }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts index c8e5bb981f921..40f7b19ea12b3 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/import_rules_schema.test.ts @@ -1747,25 +1747,31 @@ describe('import rules schema', () => { lists: [ { field: 'source.ip', - boolean_operator: 'and', - values: [ - { - name: '127.0.0.1', - type: 'value', - }, - ], + values_operator: 'included', + values_type: 'exists', }, { field: 'host.name', - boolean_operator: 'and not', + values_operator: 'excluded', + values_type: 'match', values: [ { name: 'rock01', - type: 'value', }, + ], + and: [ { - name: 'mothra', - type: 'value', + field: 'host.id', + values_operator: 'included', + values_type: 'match_all', + values: [ + { + name: '123', + }, + { + name: '678', + }, + ], }, ], }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/patch_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/patch_rules_schema.test.ts index 45b5028f392b9..e01a8f40fcea4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/patch_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/patch_rules_schema.test.ts @@ -1229,25 +1229,31 @@ describe('patch rules schema', () => { lists: [ { field: 'source.ip', - boolean_operator: 'and', - values: [ - { - name: '127.0.0.1', - type: 'value', - }, - ], + values_operator: 'included', + values_type: 'exists', }, { field: 'host.name', - boolean_operator: 'and not', + values_operator: 'excluded', + values_type: 'match', values: [ { name: 'rock01', - type: 'value', }, + ], + and: [ { - name: 'mothra', - type: 'value', + field: 'host.id', + values_operator: 'included', + values_type: 'match_all', + values: [ + { + name: '123', + }, + { + name: '678', + }, + ], }, ], }, @@ -1263,25 +1269,28 @@ describe('patch rules schema', () => { lists: [ { field: 'source.ip', - boolean_operator: 'and', - values: [ - { - name: '127.0.0.1', - type: 'value', - }, - ], + values_operator: 'included', + values_type: 'exists', }, { field: 'host.name', - boolean_operator: 'and not', + values_operator: 'excluded', + values_type: 'match', values: [ { name: 'rock01', - type: 'value', }, + ], + and: [ { - name: 'mothra', - type: 'value', + field: 'host.id', + values_operator: 'included', + values_type: 'match_all', + values: [ + { + name: '123', + }, + ], }, ], }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/response/__mocks__/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/response/__mocks__/utils.ts index 46cd1b653b5b4..d5ea950d163f5 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/response/__mocks__/utils.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/response/__mocks__/utils.ts @@ -66,25 +66,31 @@ export const getBaseResponsePayload = (anchorDate: string = ANCHOR_DATE): RulesS lists: [ { field: 'source.ip', - boolean_operator: 'and', - values: [ - { - name: '127.0.0.1', - type: 'value', - }, - ], + values_operator: 'included', + values_type: 'exists', }, { field: 'host.name', - boolean_operator: 'and not', + values_operator: 'excluded', + values_type: 'match', values: [ { name: 'rock01', - type: 'value', }, + ], + and: [ { - name: 'mothra', - type: 'value', + field: 'host.id', + values_operator: 'included', + values_type: 'match_all', + values: [ + { + name: '123', + }, + { + name: '678', + }, + ], }, ], }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/response/schemas.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/response/schemas.ts index 538c8f754fd6e..faae1dde83545 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/response/schemas.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/response/schemas.ts @@ -154,13 +154,34 @@ export const note = t.string; // NOTE: Experimental list support not being shipped currently and behind a feature flag // TODO: Remove this comment once we lists have passed testing and is ready for the release -export const boolean_operator = t.keyof({ and: null, 'and not': null }); -export const list_type = t.keyof({ value: null }); // TODO: (LIST-FEATURE) Eventually this can include "list" when we support lists CRUD -export const list_value = t.exact(t.type({ name: t.string, type: list_type })); +export const list_field = t.string; +export const list_values_operator = t.keyof({ included: null, excluded: null }); +export const list_values_type = t.keyof({ match: null, match_all: null, list: null, exists: null }); +export const list_values = t.exact( + t.intersection([ + t.type({ + name: t.string, + }), + t.partial({ + id: t.string, + description: t.string, + created_at, + }), + ]) +); export const list = t.exact( - t.type({ - field: t.string, - boolean_operator, - values: t.array(list_value), - }) + t.intersection([ + t.type({ + field: t.string, + values_operator: list_values_operator, + values_type: list_values_type, + }), + t.partial({ values: t.array(list_values) }), + ]) ); +export const list_and = t.intersection([ + list, + t.partial({ + and: t.array(list), + }), +]); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/schemas.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/schemas.ts index 16e419f389f09..c17ae8466a86c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/schemas.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/schemas.ts @@ -126,12 +126,28 @@ export const note = Joi.string(); // NOTE: Experimental list support not being shipped currently and behind a feature flag // TODO: (LIST-FEATURE) Remove this comment once we lists have passed testing and is ready for the release -export const boolean_operator = Joi.string().valid('and', 'and not'); -export const list_type = Joi.string().valid('value'); // TODO: (LIST-FEATURE) Eventually this can be "list" when we support list types -export const list_value = Joi.object({ name: Joi.string().required(), type: list_type.required() }); +export const list_field = Joi.string(); +export const list_values_operator = Joi.string().valid(['included', 'excluded']); +export const list_values_types = Joi.string().valid(['match', 'match_all', 'list', 'exists']); +export const list_values = Joi.object({ + name: Joi.string().required(), + id: Joi.string(), + description: Joi.string(), + created_at, +}); export const list = Joi.object({ - field: Joi.string().required(), - boolean_operator: boolean_operator.required(), - values: Joi.array().items(list_value), + field: list_field.required(), + values_operator: list_values_operator.required(), + values_type: list_values_types.required(), + values: Joi.when('values_type', { + is: 'exists', + then: Joi.forbidden(), + otherwise: Joi.array() + .items(list_values) + .required(), + }), +}); +export const list_and = Joi.object({ + and: Joi.array().items(list), }); -export const lists = Joi.array().items(list); +export const lists = Joi.array().items(list.concat(list_and)); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.test.ts index 14df1c3d8cd55..e8a9c7b0886a1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.test.ts @@ -23,25 +23,79 @@ describe('lists_default_array', () => { const payload = [ { field: 'source.ip', - boolean_operator: 'and', + values_operator: 'included', + values_type: 'exists', + }, + { + field: 'host.name', + values_operator: 'excluded', + values_type: 'match', values: [ { - name: '127.0.0.1', - type: 'value', + name: 'rock01', + }, + ], + and: [ + { + field: 'host.id', + values_operator: 'included', + values_type: 'match_all', + values: [ + { + name: '123', + }, + { + name: '678', + }, + ], }, ], }, + ]; + const decoded = ListsDefaultArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate an array of lists that includes a values_operator other than included or excluded', () => { + const payload = [ + { + field: 'source.ip', + values_operator: 'included', + values_type: 'exists', + }, + { + field: 'host.name', + values_operator: 'excluded', + values_type: 'exists', + }, + { + field: 'host.hostname', + values_operator: 'jibber jabber', + values_type: 'exists', + }, + ]; + const decoded = ListsDefaultArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "jibber jabber" supplied to "values_operator"', + ]); + expect(message.schema).toEqual({}); + }); + + // TODO - this scenario should never come up, as the values key is forbidden when values_type is "exists" in the incoming schema - need to find a good way to do this in io-ts + test('it will validate an array of lists that includes "values" when "values_type" is "exists"', () => { + const payload = [ { field: 'host.name', - boolean_operator: 'and not', + values_operator: 'excluded', + values_type: 'exists', values: [ { - name: 'rock01', - type: 'value', - }, - { - name: 'mothra', - type: 'value', + name: '127.0.0.1', }, ], }, @@ -53,15 +107,63 @@ describe('lists_default_array', () => { expect(message.schema).toEqual(payload); }); + // TODO - this scenario should never come up, as the values key is required when values_type is "match" in the incoming schema - need to find a good way to do this in io-ts + test('it will validate an array of lists that does not include "values" when "values_type" is "match"', () => { + const payload = [ + { + field: 'host.name', + values_operator: 'excluded', + values_type: 'match', + }, + ]; + const decoded = ListsDefaultArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + // TODO - this scenario should never come up, as the values key is required when values_type is "match_all" in the incoming schema - need to find a good way to do this in io-ts + test('it will validate an array of lists that does not include "values" when "values_type" is "match_all"', () => { + const payload = [ + { + field: 'host.name', + values_operator: 'excluded', + values_type: 'match_all', + }, + ]; + const decoded = ListsDefaultArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + // TODO - this scenario should never come up, as the values key is required when values_type is "list" in the incoming schema - need to find a good way to do this in io-ts + test('it should not validate an array of lists that does not include "values" when "values_type" is "list"', () => { + const payload = [ + { + field: 'host.name', + values_operator: 'excluded', + values_type: 'list', + }, + ]; + const decoded = ListsDefaultArray.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + test('it should not validate an array with a number', () => { const payload = [ { field: 'source.ip', - boolean_operator: 'and', + values_operator: 'included', + values_type: 'exists', values: [ { name: '127.0.0.1', - type: 'value', }, ], }, @@ -70,7 +172,10 @@ describe('lists_default_array', () => { const decoded = ListsDefaultArray.decode(payload); const message = pipe(decoded, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual(['Invalid value "5" supplied to ""']); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "5" supplied to ""', + 'Invalid value "5" supplied to ""', + ]); expect(message.schema).toEqual({}); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.ts index 0e0944a11b416..85a38e296494a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/types/lists_default_array.ts @@ -7,10 +7,10 @@ import * as t from 'io-ts'; import { Either } from 'fp-ts/lib/Either'; -import { list } from '../response/schemas'; +import { list_and as listAnd } from '../response/schemas'; export type ListsDefaultArrayC = t.Type; -type List = t.TypeOf; +type List = t.TypeOf; /** * Types the ListsDefaultArray as: @@ -18,9 +18,9 @@ type List = t.TypeOf; */ export const ListsDefaultArray: ListsDefaultArrayC = new t.Type( 'listsWithDefaultArray', - t.array(list).is, + t.array(listAnd).is, (input): Either => - input == null ? t.success([]) : t.array(list).decode(input), + input == null ? t.success([]) : t.array(listAnd).decode(input), t.identity ); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts index 6f6beea7fa5fb..e8f9aad620ca0 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas/update_rules_schema.test.ts @@ -1552,25 +1552,28 @@ describe('create rules schema', () => { lists: [ { field: 'source.ip', - boolean_operator: 'and', - values: [ - { - name: '127.0.0.1', - type: 'value', - }, - ], + values_operator: 'included', + values_type: 'exists', }, { field: 'host.name', - boolean_operator: 'and not', + values_operator: 'excluded', + values_type: 'match', values: [ { name: 'rock01', - type: 'value', }, + ], + and: [ { - name: 'mothra', - type: 'value', + field: 'host.id', + values_operator: 'included', + values_type: 'match_all', + values: [ + { + name: '123', + }, + ], }, ], }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts index 8d7360bae8eb9..e4015ad8bafa4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts @@ -309,7 +309,7 @@ export const validateLicenseForRuleType = ({ }: { license: ILicense; ruleType: RuleType; -}) => { +}): void => { if (isMlRule(ruleType) && !license.hasAtLeast(MINIMUM_ML_LICENSE)) { const message = i18n.translate('xpack.siem.licensing.unsupportedMachineLearningMessage', { defaultMessage: diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/create_rule_actions_saved_object.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/create_rule_actions_saved_object.ts index 23c99b36cb4a7..991690d901d8a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/create_rule_actions_saved_object.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/create_rule_actions_saved_object.ts @@ -9,12 +9,13 @@ import { AlertServices } from '../../../../../../../plugins/alerting/server'; import { ruleActionsSavedObjectType } from './saved_object_mappings'; import { IRuleActionsAttributesSavedObjectAttributes } from './types'; import { getThrottleOptions, getRuleActionsFromSavedObject } from './utils'; +import { RulesActionsSavedObject } from './get_rule_actions_saved_object'; interface CreateRuleActionsSavedObject { ruleAlertId: string; savedObjectsClient: AlertServices['savedObjectsClient']; actions: RuleAlertAction[] | undefined; - throttle: string | undefined; + throttle: string | null | undefined; } export const createRuleActionsSavedObject = async ({ @@ -22,7 +23,7 @@ export const createRuleActionsSavedObject = async ({ savedObjectsClient, actions = [], throttle, -}: CreateRuleActionsSavedObject) => { +}: CreateRuleActionsSavedObject): Promise => { const ruleActionsSavedObject = await savedObjectsClient.create< IRuleActionsAttributesSavedObjectAttributes >(ruleActionsSavedObjectType, { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/delete_rule_actions_saved_object.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/delete_rule_actions_saved_object.ts index 4e8781dd45692..91489334940bd 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/delete_rule_actions_saved_object.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/delete_rule_actions_saved_object.ts @@ -16,10 +16,11 @@ interface DeleteRuleActionsSavedObject { export const deleteRuleActionsSavedObject = async ({ ruleAlertId, savedObjectsClient, -}: DeleteRuleActionsSavedObject) => { +}: DeleteRuleActionsSavedObject): Promise<{} | null> => { const ruleActions = await getRuleActionsSavedObject({ ruleAlertId, savedObjectsClient }); - - if (!ruleActions) return null; - - return savedObjectsClient.delete(ruleActionsSavedObjectType, ruleActions.id); + if (ruleActions != null) { + return savedObjectsClient.delete(ruleActionsSavedObjectType, ruleActions.id); + } else { + return null; + } }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/get_rule_actions_saved_object.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/get_rule_actions_saved_object.ts index 3ae9090526d69..dad35f6cb1f96 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/get_rule_actions_saved_object.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/get_rule_actions_saved_object.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { RuleAlertAction } from '../../../../common/detection_engine/types'; import { AlertServices } from '../../../../../../../plugins/alerting/server'; import { ruleActionsSavedObjectType } from './saved_object_mappings'; import { IRuleActionsAttributesSavedObjectAttributes } from './types'; @@ -14,10 +15,17 @@ interface GetRuleActionsSavedObject { savedObjectsClient: AlertServices['savedObjectsClient']; } +export interface RulesActionsSavedObject { + id: string; + actions: RuleAlertAction[]; + alertThrottle: string | null; + ruleThrottle: string; +} + export const getRuleActionsSavedObject = async ({ ruleAlertId, savedObjectsClient, -}: GetRuleActionsSavedObject) => { +}: GetRuleActionsSavedObject): Promise => { const { saved_objects } = await savedObjectsClient.find< IRuleActionsAttributesSavedObjectAttributes >({ @@ -29,7 +37,7 @@ export const getRuleActionsSavedObject = async ({ if (!saved_objects[0]) { return null; + } else { + return getRuleActionsFromSavedObject(saved_objects[0]); } - - return getRuleActionsFromSavedObject(saved_objects[0]); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/update_or_create_rule_actions_saved_object.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/update_or_create_rule_actions_saved_object.ts index 3856f75255262..d79c61f6200e3 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/update_or_create_rule_actions_saved_object.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/update_or_create_rule_actions_saved_object.ts @@ -15,7 +15,7 @@ interface UpdateOrCreateRuleActionsSavedObject { ruleAlertId: string; savedObjectsClient: AlertServices['savedObjectsClient']; actions: RuleAlertAction[] | undefined; - throttle: string | undefined; + throttle: string | null | undefined; } export const updateOrCreateRuleActionsSavedObject = async ({ @@ -24,16 +24,17 @@ export const updateOrCreateRuleActionsSavedObject = async ({ actions, throttle, }: UpdateOrCreateRuleActionsSavedObject): Promise => { - const currentRuleActions = await getRuleActionsSavedObject({ ruleAlertId, savedObjectsClient }); + const ruleActions = await getRuleActionsSavedObject({ ruleAlertId, savedObjectsClient }); - if (currentRuleActions) { + if (ruleActions != null) { return updateRuleActionsSavedObject({ ruleAlertId, savedObjectsClient, actions, throttle, - }) as Promise; + ruleActions, + }); + } else { + return createRuleActionsSavedObject({ ruleAlertId, savedObjectsClient, actions, throttle }); } - - return createRuleActionsSavedObject({ ruleAlertId, savedObjectsClient, actions, throttle }); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/update_rule_actions_saved_object.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/update_rule_actions_saved_object.ts index 56bce3c8b67a3..2a2c84838ed93 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/update_rule_actions_saved_object.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/update_rule_actions_saved_object.ts @@ -6,7 +6,7 @@ import { AlertServices } from '../../../../../../../plugins/alerting/server'; import { ruleActionsSavedObjectType } from './saved_object_mappings'; -import { getRuleActionsSavedObject } from './get_rule_actions_saved_object'; +import { RulesActionsSavedObject } from './get_rule_actions_saved_object'; import { RuleAlertAction } from '../../../../common/detection_engine/types'; import { getThrottleOptions } from './utils'; import { IRuleActionsAttributesSavedObjectAttributes } from './types'; @@ -15,7 +15,8 @@ interface DeleteRuleActionsSavedObject { ruleAlertId: string; savedObjectsClient: AlertServices['savedObjectsClient']; actions: RuleAlertAction[] | undefined; - throttle: string | undefined; + throttle: string | null | undefined; + ruleActions: RulesActionsSavedObject; } export const updateRuleActionsSavedObject = async ({ @@ -23,11 +24,8 @@ export const updateRuleActionsSavedObject = async ({ savedObjectsClient, actions, throttle, -}: DeleteRuleActionsSavedObject) => { - const ruleActions = await getRuleActionsSavedObject({ ruleAlertId, savedObjectsClient }); - - if (!ruleActions) return null; - + ruleActions, +}: DeleteRuleActionsSavedObject): Promise => { const throttleOptions = throttle ? getThrottleOptions(throttle) : { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/utils.ts index 3c297ed848555..554c2b4a3dd90 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/utils.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rule_actions/utils.ts @@ -5,16 +5,27 @@ */ import { SavedObjectsUpdateResponse } from 'kibana/server'; +import { RuleAlertAction } from '../../../../common/detection_engine/types'; import { IRuleActionsAttributesSavedObjectAttributes } from './types'; -export const getThrottleOptions = (throttle = 'no_actions') => ({ - ruleThrottle: throttle, - alertThrottle: ['no_actions', 'rule'].includes(throttle) ? null : throttle, +export const getThrottleOptions = ( + throttle: string | undefined | null = 'no_actions' +): { + ruleThrottle: string; + alertThrottle: string | null; +} => ({ + ruleThrottle: throttle ?? 'no_actions', + alertThrottle: ['no_actions', 'rule'].includes(throttle ?? 'no_actions') ? null : throttle, }); export const getRuleActionsFromSavedObject = ( savedObject: SavedObjectsUpdateResponse -) => ({ +): { + id: string; + actions: RuleAlertAction[]; + alertThrottle: string | null; + ruleThrottle: string; +} => ({ id: savedObject.id, actions: savedObject.attributes.actions || [], alertThrottle: savedObject.attributes.alertThrottle || null, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.test.ts index 4c8d0f51f251b..a60f1d4177978 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.test.ts @@ -34,6 +34,7 @@ describe('createRules', () => { interval: '', name: '', tags: [], + actions: [], }); expect(alertsClient.create).toHaveBeenCalledWith( diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts index bebf4f350483b..91effb4741b8b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/create_rules.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; import { Alert } from '../../../../../../../plugins/alerting/common'; import { APP_ID, SIGNALS_ID } from '../../../../common/constants'; import { CreateRuleParams } from './types'; @@ -42,6 +43,7 @@ export const createRules = async ({ note, version, lists, + actions, }: CreateRuleParams): Promise => { // TODO: Remove this and use regular lists once the feature is stable for a release const listsParam = hasListsFeature() ? { lists } : {}; @@ -81,7 +83,7 @@ export const createRules = async ({ }, schedule: { interval }, enabled, - actions: [], + actions: actions.map(transformRuleToAlertAction), throttle: null, }, }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts index ca6fb15e1fad9..dd004e3685b1d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts @@ -82,25 +82,31 @@ describe('getExportAll', () => { lists: [ { field: 'source.ip', - boolean_operator: 'and', - values: [ - { - name: '127.0.0.1', - type: 'value', - }, - ], + values_operator: 'included', + values_type: 'exists', }, { field: 'host.name', - boolean_operator: 'and not', + values_operator: 'excluded', + values_type: 'match', values: [ { name: 'rock01', - type: 'value', }, + ], + and: [ { - name: 'mothra', - type: 'value', + field: 'host.id', + values_operator: 'included', + values_type: 'match_all', + values: [ + { + name: '123', + }, + { + name: '678', + }, + ], }, ], }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts index 175c906f7996c..715cb23e8444a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_by_object_ids.test.ts @@ -90,25 +90,31 @@ describe('get_export_by_object_ids', () => { lists: [ { field: 'source.ip', - boolean_operator: 'and', - values: [ - { - name: '127.0.0.1', - type: 'value', - }, - ], + values_operator: 'included', + values_type: 'exists', }, { field: 'host.name', - boolean_operator: 'and not', + values_operator: 'excluded', + values_type: 'match', values: [ { name: 'rock01', - type: 'value', }, + ], + and: [ { - name: 'mothra', - type: 'value', + field: 'host.id', + values_operator: 'included', + values_type: 'match_all', + values: [ + { + name: '123', + }, + { + name: '678', + }, + ], }, ], }, @@ -212,25 +218,31 @@ describe('get_export_by_object_ids', () => { lists: [ { field: 'source.ip', - boolean_operator: 'and', - values: [ - { - name: '127.0.0.1', - type: 'value', - }, - ], + values_operator: 'included', + values_type: 'exists', }, { field: 'host.name', - boolean_operator: 'and not', + values_operator: 'excluded', + values_type: 'match', values: [ { name: 'rock01', - type: 'value', }, + ], + and: [ { - name: 'mothra', - type: 'value', + field: 'host.id', + values_operator: 'included', + values_type: 'match_all', + values: [ + { + name: '123', + }, + { + name: '678', + }, + ], }, ], }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts index bcbe460fb6a66..6d4bacb9cc243 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/install_prepacked_rules.ts @@ -83,6 +83,7 @@ export const installPrepackagedRules = ( note, version, lists, + actions: [], // At this time there is no pre-packaged actions }), ]; }, []); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts index d7655a15499eb..5c4889ec5fd68 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/patch_rules.ts @@ -120,7 +120,7 @@ export const patchRules = async ({ id: rule.id, data: { tags: addTags(tags ?? rule.tags, rule.params.ruleId, immutable ?? rule.params.immutable), - throttle: rule.throttle, + throttle: null, name: calculateName({ updatedName: name, originalName: rule.name }), schedule: { interval: calculateInterval(interval, rule.schedule.interval), diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts index 38b1097a845f8..b1bed5d716155 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/types.ts @@ -142,12 +142,12 @@ export interface Clients { actionsClient: ActionsClient; } -export type PatchRuleParams = Partial> & { +export type PatchRuleParams = Partial> & { id: string | undefined | null; savedObjectsClient: SavedObjectsClientContract; } & Clients; -export type UpdateRuleParams = Omit & { +export type UpdateRuleParams = Omit & { id: string | undefined | null; savedObjectsClient: SavedObjectsClientContract; } & Clients; @@ -157,7 +157,7 @@ export type DeleteRuleParams = Clients & { ruleId: string | undefined | null; }; -export type CreateRuleParams = Omit & { +export type CreateRuleParams = Omit & { ruleId: string; } & Clients; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rule_actions.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rule_actions.ts deleted file mode 100644 index ac10143c1d8d0..0000000000000 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rule_actions.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 { AlertsClient, AlertServices } from '../../../../../../../plugins/alerting/server'; -import { getRuleActionsSavedObject } from '../rule_actions/get_rule_actions_saved_object'; -import { readRules } from './read_rules'; -import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; - -interface UpdateRuleActions { - alertsClient: AlertsClient; - savedObjectsClient: AlertServices['savedObjectsClient']; - ruleAlertId: string; -} - -export const updateRuleActions = async ({ - alertsClient, - savedObjectsClient, - ruleAlertId, -}: UpdateRuleActions) => { - const rule = await readRules({ alertsClient, id: ruleAlertId }); - if (rule == null) { - return null; - } - - const ruleActions = await getRuleActionsSavedObject({ - savedObjectsClient, - ruleAlertId, - }); - - if (!ruleActions) { - return null; - } - - return alertsClient.update({ - id: ruleAlertId, - data: { - actions: !ruleActions.alertThrottle - ? ruleActions.actions.map(transformRuleToAlertAction) - : [], - throttle: null, - name: rule.name, - tags: rule.tags, - schedule: rule.schedule, - params: rule.params, - }, - }); -}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.test.ts index ca299db6ace50..72f4cbcbe68e8 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.test.ts @@ -35,6 +35,7 @@ describe('updateRules', () => { interval: '', name: '', tags: [], + actions: [], }); expect(alertsClient.disable).toHaveBeenCalledWith( @@ -61,6 +62,7 @@ describe('updateRules', () => { interval: '', name: '', tags: [], + actions: [], }); expect(alertsClient.enable).toHaveBeenCalledWith( @@ -89,6 +91,7 @@ describe('updateRules', () => { interval: '', name: '', tags: [], + actions: [], }); expect(alertsClient.update).toHaveBeenCalledWith( diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts index 0e70e05f4de78..99326768ed33b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { transformRuleToAlertAction } from '../../../../common/detection_engine/transform_actions'; import { PartialAlert } from '../../../../../../../plugins/alerting/server'; import { readRules } from './read_rules'; import { IRuleSavedAttributesSavedObjectAttributes, UpdateRuleParams } from './types'; @@ -46,6 +47,7 @@ export const updateRules = async ({ lists, anomalyThreshold, machineLearningJobId, + actions, }: UpdateRuleParams): Promise => { const rule = await readRules({ alertsClient, ruleId, id }); if (rule == null) { @@ -90,8 +92,8 @@ export const updateRules = async ({ tags: addTags(tags, rule.params.ruleId, rule.params.immutable), name, schedule: { interval }, - actions: rule.actions, - throttle: rule.throttle, + actions: actions.map(transformRuleToAlertAction), + throttle: null, params: { description, ruleId: rule.params.ruleId, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules_notifications.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules_notifications.ts index f70c591243a76..994a54048b71a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules_notifications.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/update_rules_notifications.ts @@ -8,14 +8,14 @@ import { RuleAlertAction } from '../../../../common/detection_engine/types'; import { AlertsClient, AlertServices } from '../../../../../../../plugins/alerting/server'; import { updateOrCreateRuleActionsSavedObject } from '../rule_actions/update_or_create_rule_actions_saved_object'; import { updateNotifications } from '../notifications/update_notifications'; -import { updateRuleActions } from './update_rule_actions'; +import { RuleActions } from '../rule_actions/types'; interface UpdateRulesNotifications { alertsClient: AlertsClient; savedObjectsClient: AlertServices['savedObjectsClient']; ruleAlertId: string; actions: RuleAlertAction[] | undefined; - throttle: string | undefined; + throttle: string | null | undefined; enabled: boolean; name: string; } @@ -28,7 +28,7 @@ export const updateRulesNotifications = async ({ enabled, name, throttle, -}: UpdateRulesNotifications) => { +}: UpdateRulesNotifications): Promise => { const ruleActions = await updateOrCreateRuleActionsSavedObject({ savedObjectsClient, ruleAlertId, @@ -36,19 +36,13 @@ export const updateRulesNotifications = async ({ throttle, }); - await updateRuleActions({ - alertsClient, - savedObjectsClient, - ruleAlertId, - }); - await updateNotifications({ alertsClient, ruleAlertId, enabled, name, actions: ruleActions.actions, - interval: ruleActions?.alertThrottle, + interval: ruleActions.alertThrottle, }); return ruleActions; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_action_instances.sh b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_action_instances.sh index 7804439ce0734..750c5574f4a72 100755 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_action_instances.sh +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_action_instances.sh @@ -13,5 +13,5 @@ set -e # https://github.com/elastic/kibana/blob/master/x-pack/legacy/plugins/actions/README.md#get-apiaction_find-find-actions curl -s -k \ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ - -X GET ${KIBANA_URL}${SPACE_URL}/api/action/_find \ + -X GET ${KIBANA_URL}${SPACE_URL}/api/action/_getAll \ | jq . diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/patches/update_list.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/patches/update_list.json index 8c86f4c85af1d..4db8724db4e13 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/patches/update_list.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/patches/update_list.json @@ -3,21 +3,28 @@ "lists": [ { "field": "source.ip", - "boolean_operator": "and", - "values": [ - { - "name": "127.0.0.1", - "type": "value" - } - ] + "values_operator": "excluded", + "values_type": "exists" }, { "field": "host.name", - "boolean_operator": "and not", + "values_operator": "included", + "values_type": "match", "values": [ { - "name": "rock01", - "type": "value" + "name": "rock01" + } + ], + "and": [ + { + "field": "host.id", + "values_operator": "included", + "values_type": "match_all", + "values": [ + { + "name": "123456" + } + ] } ] } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_list.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_list.json index f6856eec59966..997d03369a699 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_list.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/queries/query_with_list.json @@ -9,25 +9,31 @@ "lists": [ { "field": "source.ip", - "boolean_operator": "and", - "values": [ - { - "name": "127.0.0.1", - "type": "value" - } - ] + "values_operator": "included", + "values_type": "exists" }, { "field": "host.name", - "boolean_operator": "and not", + "values_operator": "excluded", + "values_type": "match", "values": [ { - "name": "rock01", - "type": "value" - }, + "name": "rock01" + } + ], + "and": [ { - "name": "mothra", - "type": "value" + "field": "host.id", + "values_operator": "included", + "values_type": "match_all", + "values": [ + { + "name": "123" + }, + { + "name": "678" + } + ] } ] } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_list.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_list.json index 6704c9676fa56..66b198974f574 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_list.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/rules/updates/update_list.json @@ -9,21 +9,28 @@ "lists": [ { "field": "source.ip", - "boolean_operator": "and", - "values": [ - { - "name": "127.0.0.1", - "type": "value" - } - ] + "values_operator": "excluded", + "values_type": "exists" }, { "field": "host.name", - "boolean_operator": "and not", + "values_operator": "included", + "values_type": "match", "values": [ { - "name": "rock01", - "type": "value" + "name": "rock01" + } + ], + "and": [ + { + "field": "host.id", + "values_operator": "included", + "values_type": "match_all", + "values": [ + { + "name": "123456" + } + ] } ] } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts index 6d7d7e93d7e6e..7a211c5631da6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/__mocks__/es_results.ts @@ -47,25 +47,31 @@ export const sampleRuleAlertParams = ( lists: [ { field: 'source.ip', - boolean_operator: 'and', - values: [ - { - name: '127.0.0.1', - type: 'value', - }, - ], + values_operator: 'included', + values_type: 'exists', }, { field: 'host.name', - boolean_operator: 'and not', + values_operator: 'excluded', + values_type: 'match', values: [ { name: 'rock01', - type: 'value', }, + ], + and: [ { - name: 'mothra', - type: 'value', + field: 'host.id', + values_operator: 'included', + values_type: 'match_all', + values: [ + { + name: '123', + }, + { + name: '678', + }, + ], }, ], }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_bulk_body.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_bulk_body.test.ts index f2c2b99bdac8c..f1729e35ce1f0 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_bulk_body.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_bulk_body.test.ts @@ -93,25 +93,31 @@ describe('buildBulkBody', () => { lists: [ { field: 'source.ip', - boolean_operator: 'and', - values: [ - { - name: '127.0.0.1', - type: 'value', - }, - ], + values_operator: 'included', + values_type: 'exists', }, { field: 'host.name', - boolean_operator: 'and not', + values_operator: 'excluded', + values_type: 'match', values: [ { name: 'rock01', - type: 'value', }, + ], + and: [ { - name: 'mothra', - type: 'value', + field: 'host.id', + values_operator: 'included', + values_type: 'match_all', + values: [ + { + name: '123', + }, + { + name: '678', + }, + ], }, ], }, @@ -213,25 +219,31 @@ describe('buildBulkBody', () => { lists: [ { field: 'source.ip', - boolean_operator: 'and', - values: [ - { - name: '127.0.0.1', - type: 'value', - }, - ], + values_operator: 'included', + values_type: 'exists', }, { field: 'host.name', - boolean_operator: 'and not', + values_operator: 'excluded', + values_type: 'match', values: [ { name: 'rock01', - type: 'value', }, + ], + and: [ { - name: 'mothra', - type: 'value', + field: 'host.id', + values_operator: 'included', + values_type: 'match_all', + values: [ + { + name: '123', + }, + { + name: '678', + }, + ], }, ], }, @@ -331,25 +343,31 @@ describe('buildBulkBody', () => { lists: [ { field: 'source.ip', - boolean_operator: 'and', - values: [ - { - name: '127.0.0.1', - type: 'value', - }, - ], + values_operator: 'included', + values_type: 'exists', }, { field: 'host.name', - boolean_operator: 'and not', + values_operator: 'excluded', + values_type: 'match', values: [ { name: 'rock01', - type: 'value', }, + ], + and: [ { - name: 'mothra', - type: 'value', + field: 'host.id', + values_operator: 'included', + values_type: 'match_all', + values: [ + { + name: '123', + }, + { + name: '678', + }, + ], }, ], }, @@ -442,25 +460,31 @@ describe('buildBulkBody', () => { lists: [ { field: 'source.ip', - boolean_operator: 'and', - values: [ - { - name: '127.0.0.1', - type: 'value', - }, - ], + values_operator: 'included', + values_type: 'exists', }, { field: 'host.name', - boolean_operator: 'and not', + values_operator: 'excluded', + values_type: 'match', values: [ { name: 'rock01', - type: 'value', }, + ], + and: [ { - name: 'mothra', - type: 'value', + field: 'host.id', + values_operator: 'included', + values_type: 'match_all', + values: [ + { + name: '123', + }, + { + name: '678', + }, + ], }, ], }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.test.ts index e360ceaf02f4d..e5183ed4df7bd 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/build_rule.test.ts @@ -82,25 +82,31 @@ describe('buildRule', () => { lists: [ { field: 'source.ip', - boolean_operator: 'and', - values: [ - { - name: '127.0.0.1', - type: 'value', - }, - ], + values_operator: 'included', + values_type: 'exists', }, { field: 'host.name', - boolean_operator: 'and not', + values_operator: 'excluded', + values_type: 'match', values: [ { name: 'rock01', - type: 'value', }, + ], + and: [ { - name: 'mothra', - type: 'value', + field: 'host.id', + values_operator: 'included', + values_type: 'match_all', + values: [ + { + name: '123', + }, + { + name: '678', + }, + ], }, ], }, @@ -159,25 +165,31 @@ describe('buildRule', () => { lists: [ { field: 'source.ip', - boolean_operator: 'and', - values: [ - { - name: '127.0.0.1', - type: 'value', - }, - ], + values_operator: 'included', + values_type: 'exists', }, { field: 'host.name', - boolean_operator: 'and not', + values_operator: 'excluded', + values_type: 'match', values: [ { name: 'rock01', - type: 'value', }, + ], + and: [ { - name: 'mothra', - type: 'value', + field: 'host.id', + values_operator: 'included', + values_type: 'match_all', + values: [ + { + name: '123', + }, + { + name: '678', + }, + ], }, ], }, @@ -235,25 +247,31 @@ describe('buildRule', () => { lists: [ { field: 'source.ip', - boolean_operator: 'and', - values: [ - { - name: '127.0.0.1', - type: 'value', - }, - ], + values_operator: 'included', + values_type: 'exists', }, { field: 'host.name', - boolean_operator: 'and not', + values_operator: 'excluded', + values_type: 'match', values: [ { name: 'rock01', - type: 'value', }, + ], + and: [ { - name: 'mothra', - type: 'value', + field: 'host.id', + values_operator: 'included', + values_type: 'match_all', + values: [ + { + name: '123', + }, + { + name: '678', + }, + ], }, ], }, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/bulk_create_ml_signals.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/bulk_create_ml_signals.ts index 355041d9efbdb..ba8938f116fc6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/bulk_create_ml_signals.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/bulk_create_ml_signals.ts @@ -10,7 +10,7 @@ import { SearchResponse } from 'elasticsearch'; import { Logger } from '../../../../../../../../src/core/server'; import { AlertServices } from '../../../../../../../plugins/alerting/server'; import { RuleAlertAction } from '../../../../common/detection_engine/types'; -import { RuleTypeParams } from '../types'; +import { RuleTypeParams, RefreshTypes } from '../types'; import { singleBulkCreate, SingleBulkCreateResponse } from './single_bulk_create'; import { AnomalyResults, Anomaly } from '../../machine_learning'; @@ -29,6 +29,7 @@ interface BulkCreateMlSignalsParams { updatedBy: string; interval: string; enabled: boolean; + refresh: RefreshTypes; tags: string[]; throttle: string; } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts index 414270ffcdd5c..81600b0b8dd9b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts @@ -52,6 +52,7 @@ describe('searchAfterAndBulkCreate', () => { enabled: true, pageSize: 1, filter: undefined, + refresh: false, tags: ['some fake tag 1', 'some fake tag 2'], throttle: 'no_actions', }); @@ -126,6 +127,7 @@ describe('searchAfterAndBulkCreate', () => { enabled: true, pageSize: 1, filter: undefined, + refresh: false, tags: ['some fake tag 1', 'some fake tag 2'], throttle: 'no_actions', }); @@ -156,6 +158,7 @@ describe('searchAfterAndBulkCreate', () => { enabled: true, pageSize: 1, filter: undefined, + refresh: false, tags: ['some fake tag 1', 'some fake tag 2'], throttle: 'no_actions', }); @@ -198,6 +201,7 @@ describe('searchAfterAndBulkCreate', () => { enabled: true, pageSize: 1, filter: undefined, + refresh: false, tags: ['some fake tag 1', 'some fake tag 2'], throttle: 'no_actions', }); @@ -240,6 +244,7 @@ describe('searchAfterAndBulkCreate', () => { enabled: true, pageSize: 1, filter: undefined, + refresh: false, tags: ['some fake tag 1', 'some fake tag 2'], throttle: 'no_actions', }); @@ -284,6 +289,7 @@ describe('searchAfterAndBulkCreate', () => { enabled: true, pageSize: 1, filter: undefined, + refresh: false, tags: ['some fake tag 1', 'some fake tag 2'], throttle: 'no_actions', }); @@ -328,6 +334,7 @@ describe('searchAfterAndBulkCreate', () => { enabled: true, pageSize: 1, filter: undefined, + refresh: false, tags: ['some fake tag 1', 'some fake tag 2'], throttle: 'no_actions', }); @@ -374,6 +381,7 @@ describe('searchAfterAndBulkCreate', () => { enabled: true, pageSize: 1, filter: undefined, + refresh: false, tags: ['some fake tag 1', 'some fake tag 2'], throttle: 'no_actions', }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts index ff81730bc4a72..3a964cb91fbdb 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.ts @@ -6,7 +6,7 @@ import { AlertServices } from '../../../../../../../plugins/alerting/server'; import { RuleAlertAction } from '../../../../common/detection_engine/types'; -import { RuleTypeParams } from '../types'; +import { RuleTypeParams, RefreshTypes } from '../types'; import { Logger } from '../../../../../../../../src/core/server'; import { singleSearchAfter } from './single_search_after'; import { singleBulkCreate } from './single_bulk_create'; @@ -30,6 +30,7 @@ interface SearchAfterAndBulkCreateParams { enabled: boolean; pageSize: number; filter: unknown; + refresh: RefreshTypes; tags: string[]; throttle: string; } @@ -61,6 +62,7 @@ export const searchAfterAndBulkCreate = async ({ interval, enabled, pageSize, + refresh, tags, throttle, }: SearchAfterAndBulkCreateParams): Promise => { @@ -92,6 +94,7 @@ export const searchAfterAndBulkCreate = async ({ updatedBy, interval, enabled, + refresh, tags, throttle, }); @@ -179,6 +182,7 @@ export const searchAfterAndBulkCreate = async ({ updatedBy, interval, enabled, + refresh, tags, throttle, }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts index 11d31f1805440..03fb5832fdf42 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts @@ -47,7 +47,7 @@ const getPayload = ( interval: ruleAlert.schedule.interval, name: ruleAlert.name, tags: ruleAlert.tags, - throttle: ruleAlert.throttle!, + throttle: ruleAlert.throttle, scrollSize: 10, scrollLock: '0', }, @@ -105,6 +105,7 @@ describe('rules_notification_alert_type', () => { }; (ruleStatusServiceFactory as jest.Mock).mockReturnValue(ruleStatusService); (getGapBetweenRuns as jest.Mock).mockReturnValue(moment.duration(0)); + (searchAfterAndBulkCreate as jest.Mock).mockClear(); (searchAfterAndBulkCreate as jest.Mock).mockResolvedValue({ success: true, searchAfterTimes: [], @@ -149,6 +150,37 @@ describe('rules_notification_alert_type', () => { }); }); + it("should set refresh to 'wait_for' when actions are present", async () => { + const ruleAlert = getResult(); + ruleAlert.actions = [ + { + actionTypeId: '.slack', + params: { + message: + 'Rule generated {{state.signals_count}} signals\n\n{{context.rule.name}}\n{{{context.results_link}}}', + }, + group: 'default', + id: '99403909-ca9b-49ba-9d7a-7e5320e68d05', + }, + ]; + + savedObjectsClient.get.mockResolvedValue({ + id: 'id', + type: 'type', + references: [], + attributes: ruleAlert, + }); + await alert.executor(payload); + expect((searchAfterAndBulkCreate as jest.Mock).mock.calls[0][0].refresh).toEqual('wait_for'); + (searchAfterAndBulkCreate as jest.Mock).mockClear(); + }); + + it('should set refresh to false when actions are not present', async () => { + await alert.executor(payload); + expect((searchAfterAndBulkCreate as jest.Mock).mock.calls[0][0].refresh).toEqual(false); + (searchAfterAndBulkCreate as jest.Mock).mockClear(); + }); + it('should call scheduleActions if signalsCount was greater than 0 and rule has actions defined', async () => { const ruleAlert = getResult(); ruleAlert.actions = [ diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts index 417fcbbe42a56..0357f906f8035 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.ts @@ -24,7 +24,10 @@ import { signalParamsSchema } from './signal_params_schema'; import { siemRuleActionGroups } from './siem_rule_action_groups'; import { findMlSignals } from './find_ml_signals'; import { bulkCreateMlSignals } from './bulk_create_ml_signals'; -import { scheduleNotificationActions } from '../notifications/schedule_notification_actions'; +import { + scheduleNotificationActions, + NotificationRuleTypeParams, +} from '../notifications/schedule_notification_actions'; import { ruleStatusServiceFactory } from './rule_status_service'; import { buildRuleMessageFactory } from './rule_messages'; import { ruleStatusSavedObjectsClientFactory } from './rule_status_saved_objects_client'; @@ -95,6 +98,7 @@ export const signalRulesAlertType = ({ params: ruleParams, } = savedObject.attributes; const updatedAt = savedObject.updated_at ?? ''; + const refresh = actions.length ? 'wait_for' : false; const buildRuleMessage = buildRuleMessageFactory({ id: alertId, ruleId, @@ -178,6 +182,7 @@ export const signalRulesAlertType = ({ updatedAt, interval, enabled, + refresh, tags, }); result.success = success; @@ -238,6 +243,7 @@ export const signalRulesAlertType = ({ interval, enabled, pageSize: searchAfterSize, + refresh, tags, throttle, }); @@ -246,7 +252,7 @@ export const signalRulesAlertType = ({ if (result.success) { if (actions.length) { - const notificationRuleParams = { + const notificationRuleParams: NotificationRuleTypeParams = { ...ruleParams, name, id: savedObject.id, @@ -259,7 +265,7 @@ export const signalRulesAlertType = ({ from: fromInMs, to: toInMs, id: savedObject.id, - kibanaSiemAppUrl: meta?.kibanaSiemAppUrl as string, + kibanaSiemAppUrl: meta?.kibana_siem_app_url, }); logger.info( diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts index 56f061cdfa3ca..45365b446cbf0 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.test.ts @@ -159,6 +159,7 @@ describe('singleBulkCreate', () => { updatedBy: 'elastic', interval: '5m', enabled: true, + refresh: false, tags: ['some fake tag 1', 'some fake tag 2'], throttle: 'no_actions', }); @@ -192,6 +193,7 @@ describe('singleBulkCreate', () => { updatedBy: 'elastic', interval: '5m', enabled: true, + refresh: false, tags: ['some fake tag 1', 'some fake tag 2'], throttle: 'no_actions', }); @@ -217,6 +219,7 @@ describe('singleBulkCreate', () => { updatedBy: 'elastic', interval: '5m', enabled: true, + refresh: false, tags: ['some fake tag 1', 'some fake tag 2'], throttle: 'no_actions', }); @@ -243,6 +246,7 @@ describe('singleBulkCreate', () => { updatedBy: 'elastic', interval: '5m', enabled: true, + refresh: false, tags: ['some fake tag 1', 'some fake tag 2'], throttle: 'no_actions', }); @@ -271,6 +275,7 @@ describe('singleBulkCreate', () => { updatedBy: 'elastic', interval: '5m', enabled: true, + refresh: false, tags: ['some fake tag 1', 'some fake tag 2'], throttle: 'no_actions', }); @@ -365,6 +370,7 @@ describe('singleBulkCreate', () => { updatedBy: 'elastic', interval: '5m', enabled: true, + refresh: false, tags: ['some fake tag 1', 'some fake tag 2'], throttle: 'no_actions', }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts index 6dd8823b57e4d..fc33d0e15e43f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/single_bulk_create.ts @@ -9,7 +9,7 @@ import { performance } from 'perf_hooks'; import { AlertServices } from '../../../../../../../plugins/alerting/server'; import { SignalSearchResponse, BulkResponse } from './types'; import { RuleAlertAction } from '../../../../common/detection_engine/types'; -import { RuleTypeParams } from '../types'; +import { RuleTypeParams, RefreshTypes } from '../types'; import { generateId, makeFloatString } from './utils'; import { buildBulkBody } from './build_bulk_body'; import { Logger } from '../../../../../../../../src/core/server'; @@ -31,6 +31,7 @@ interface SingleBulkCreateParams { enabled: boolean; tags: string[]; throttle: string; + refresh: RefreshTypes; } /** @@ -77,6 +78,7 @@ export const singleBulkCreate = async ({ updatedBy, interval, enabled, + refresh, tags, throttle, }: SingleBulkCreateParams): Promise => { @@ -124,7 +126,7 @@ export const singleBulkCreate = async ({ const start = performance.now(); const response: BulkResponse = await services.callCluster('bulk', { index: signalsIndex, - refresh: false, + refresh, body: bulkBody, }); const end = performance.now(); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts index 543e8bf0619b0..040e32aa0d360 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/types.ts @@ -162,5 +162,10 @@ export interface AlertAttributes { } export interface RuleAlertAttributes extends AlertAttributes { - params: RuleAlertParams; + params: Omit< + RuleAlertParams, + 'ruleId' | 'name' | 'enabled' | 'interval' | 'tags' | 'actions' | 'throttle' + > & { + ruleId: string; + }; } diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/types.ts index efa0a92cc573b..035f1b10ff8b2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/types.ts @@ -29,6 +29,11 @@ export interface ThreatParams { // We don't have the input types defined through io-ts just yet but as we being introducing types from there we will more and more remove // types and share them between input and output schema but have an input Rule Schema and an output Rule Schema. +export interface Meta { + [key: string]: {} | string | undefined | null; + kibana_siem_app_url?: string | undefined; +} + export interface RuleAlertParams { actions: RuleAlertAction[]; anomalyThreshold: number | undefined; @@ -51,7 +56,7 @@ export interface RuleAlertParams { query: string | undefined | null; references: string[]; savedId?: string | undefined | null; - meta: Record | undefined | null; + meta: Meta | undefined | null; severity: string; tags: string[]; to: string; @@ -60,7 +65,7 @@ export interface RuleAlertParams { threat: ThreatParams[] | undefined | null; type: RuleType; version: number; - throttle: string; + throttle: string | undefined | null; lists: ListsDefaultArraySchema | null | undefined; } @@ -144,3 +149,5 @@ export type CallWithRequest, V> = ( params: T, options?: CallAPIOptions ) => Promise; + +export type RefreshTypes = false | 'wait_for'; diff --git a/x-pack/legacy/plugins/siem/server/lib/index_fields/elasticsearch_adapter.test.ts b/x-pack/legacy/plugins/siem/server/lib/index_fields/elasticsearch_adapter.test.ts index 93472b8539efd..20bc1387a3c4e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/index_fields/elasticsearch_adapter.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/index_fields/elasticsearch_adapter.test.ts @@ -23,77 +23,108 @@ describe('Index Fields', () => { ).toEqual( sortBy('name', [ { - aggregatable: true, - 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.', + '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', - indexes: ['auditbeat', 'filebeat', 'packetbeat'], name: '@timestamp', - searchable: true, type: 'date', + searchable: true, + aggregatable: true, + category: 'base', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], }, { + 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', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + }, + { + 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, - category: 'agent', + readFromDocValues: true, + category: '_index', + indexes: ['auditbeat', 'filebeat', 'packetbeat'], + }, + { description: - 'Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but `agent.id` does not.', + 'Ephemeral identifier of this agent (if one exists).\n\nThis id normally changes across restarts, but `agent.id` does not.', example: '8a4f500f', - indexes: ['auditbeat'], name: 'agent.ephemeral_id', - searchable: true, type: 'string', - }, - { + searchable: true, aggregatable: true, category: 'agent', - indexes: ['filebeat'], + indexes: ['auditbeat'], + }, + { name: 'agent.hostname', searchable: true, type: 'string', - }, - { aggregatable: true, category: 'agent', + indexes: ['filebeat'], + }, + { description: - 'Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.', + 'Unique identifier of this agent (if one exists).\n\nExample: For Beats this would be beat.id.', example: '8a4f500d', - indexes: ['packetbeat'], name: 'agent.id', - searchable: true, type: 'string', - }, - { + searchable: true, aggregatable: true, category: 'agent', + indexes: ['packetbeat'], + }, + { 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.', + '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', - indexes: ['auditbeat', 'filebeat'], name: 'agent.name', - searchable: true, type: 'string', - }, - { + searchable: true, aggregatable: true, category: 'agent', + indexes: ['auditbeat', 'filebeat'], + }, + { 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.', + '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', - indexes: ['auditbeat', 'packetbeat'], name: 'agent.type', - searchable: true, type: 'string', - }, - { + searchable: true, aggregatable: true, category: 'agent', + indexes: ['auditbeat', 'packetbeat'], + }, + { description: 'Version of the agent.', example: '6.0.0-rc2', - indexes: ['auditbeat', 'filebeat'], name: 'agent.version', - searchable: true, type: 'string', + searchable: true, + aggregatable: true, + category: 'agent', + indexes: ['auditbeat', 'filebeat'], }, ]) ); diff --git a/x-pack/legacy/plugins/siem/server/lib/index_fields/elasticsearch_adapter.ts b/x-pack/legacy/plugins/siem/server/lib/index_fields/elasticsearch_adapter.ts index 48b9750ddd949..7cfb5ad391149 100644 --- a/x-pack/legacy/plugins/siem/server/lib/index_fields/elasticsearch_adapter.ts +++ b/x-pack/legacy/plugins/siem/server/lib/index_fields/elasticsearch_adapter.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { get } from 'lodash/fp'; +import { isEmpty, get } from 'lodash/fp'; import { IndexField } from '../../graphql/types'; import { @@ -51,6 +51,23 @@ export class ElasticsearchIndexFieldAdapter implements FieldsAdapter { } } +const missingFields = [ + { + name: '_id', + type: 'string', + searchable: true, + aggregatable: false, + readFromDocValues: true, + }, + { + name: '_index', + type: 'string', + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, +]; + export const formatIndexFields = ( responsesIndexFields: IndexFieldDescriptor[][], indexesAlias: IndexAlias[] @@ -59,20 +76,23 @@ export const formatIndexFields = ( .reduce( (accumulator: IndexField[], indexFields: IndexFieldDescriptor[], indexesAliasIdx: number) => [ ...accumulator, - ...indexFields.reduce((itemAccumulator: IndexField[], index: IndexFieldDescriptor) => { - const alias: IndexAlias = indexesAlias[indexesAliasIdx]; - const splitName = index.name.split('.'); - const category = baseCategoryFields.includes(splitName[0]) ? 'base' : splitName[0]; - return [ - ...itemAccumulator, - { - ...(hasDocumentation(alias, index.name) ? getDocumentation(alias, index.name) : {}), - ...index, - category, - indexes: [alias], - } as IndexField, - ]; - }, []), + ...[...missingFields, ...indexFields].reduce( + (itemAccumulator: IndexField[], index: IndexFieldDescriptor) => { + const alias: IndexAlias = indexesAlias[indexesAliasIdx]; + const splitName = index.name.split('.'); + const category = baseCategoryFields.includes(splitName[0]) ? 'base' : splitName[0]; + return [ + ...itemAccumulator, + { + ...(hasDocumentation(alias, index.name) ? getDocumentation(alias, index.name) : {}), + ...index, + category, + indexes: [alias], + } as IndexField, + ]; + }, + [] + ), ], [] ) @@ -84,7 +104,10 @@ export const formatIndexFields = ( ...accumulator.slice(0, alreadyExistingIndexField), { ...existingIndexField, - indexes: [...existingIndexField.indexes, ...indexfield.indexes], + description: isEmpty(existingIndexField.description) + ? indexfield.description + : existingIndexField.description, + indexes: Array.from(new Set([...existingIndexField.indexes, ...indexfield.indexes])), }, ...accumulator.slice(alreadyExistingIndexField + 1), ]; diff --git a/x-pack/legacy/plugins/siem/server/utils/beat_schema/8.0.0/auditbeat.ts b/x-pack/legacy/plugins/siem/server/utils/beat_schema/8.0.0/auditbeat.ts index 773d4ed6a7e95..76c865679dd05 100644 --- a/x-pack/legacy/plugins/siem/server/utils/beat_schema/8.0.0/auditbeat.ts +++ b/x-pack/legacy/plugins/siem/server/utils/beat_schema/8.0.0/auditbeat.ts @@ -15,91 +15,129 @@ export const auditbeatSchema: Schema = [ { key: 'ecs', title: 'ECS', - description: 'ECS fields.', + description: 'ECS Fields.', fields: [ { name: '@timestamp', - type: 'date', level: 'core', required: true, - example: '2016-05-23T08:05:34.853Z', + type: 'date', 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.', - }, - { - name: 'tags', - level: 'core', - type: 'keyword', - example: '["production", "env2"]', - description: 'List of keywords used to tag each event.', + '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', - example: { - env: 'production', - application: 'foo-bar', - }, + object_type: 'keyword', description: - '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.', + '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', - example: 'Hello World', description: - 'For log events the message field contains the log message. In other use cases the message field can be used to concatenate different values which are then freely searchable. If multiple messages exist, they can be combined into one message.', + '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 any, that collects, detects, or observes events on a host, or takes measurements on a host. Examples include Beats. Agents may also run on observers. ECS agent.* fields shall be populated with details of the agent running on the host or observer where the event happened or the measurement was taken.', + '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. For APM, it is the agent running in the app/service. The agent information does not change if data is sent through queuing systems like Kafka, Redis, or processing systems such as Logstash or APM Server.', + '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: 'version', + 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', - description: 'Version of the agent.', - example: '6.0.0-rc2', + 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: - '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.', + '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. 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.', + '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: 'id', + 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 identifier of this agent (if one exists). Example: For Beats this would be beat.id.', - example: '8a4f500d', + 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', + example: 15169, }, { - name: 'ephemeral_id', + name: 'organization.name', level: 'extended', type: 'keyword', - description: - 'Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but `agent.id` does not.', - example: '8a4f500f', + ignore_above: 1024, + multi_fields: [ + { + name: 'text', + type: 'text', + norms: false, + default_field: false, + }, + ], + description: 'Organization name.', + example: 'Google LLC', }, ], }, @@ -108,4693 +146,7750 @@ export const auditbeatSchema: Schema = [ title: 'Client', group: 2, description: - 'A client is defined as the initiator of a network connection for events regarding sessions, connections, or bidirectional flow records. For TCP events, the client is the initiator of the TCP connection that sends the SYN packet(s). For other protocols, the client is generally the initiator or requestor in the network transaction. Some systems use the term "originator" to refer the client in TCP connections. The client fields describe details about the system acting as the client in the network event. Client fields are usually populated in conjunction with server fields. Client fields are generally not populated for packet-level events. Client / server representations can add semantic context to an exchange, which is helpful to visualize the data in certain situations. If your context falls in that category, you should still ensure that source and destination are filled appropriately.', + '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 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.', + '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: 'ip', - level: 'core', - type: 'ip', - description: 'IP address of the client. Can be one or multiple IPv4 or IPv6 addresses.', + 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: 'port', + 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', - description: 'Port of the client.', + format: 'bytes', + description: 'Bytes sent from the client to the server.', + example: 184, }, { - name: 'mac', + name: 'domain', level: 'core', type: 'keyword', - description: 'MAC address of the client.', + ignore_above: 1024, + description: 'Client domain.', }, { - name: 'domain', + name: 'geo.city_name', level: 'core', type: 'keyword', - description: 'Client domain.', + ignore_above: 1024, + description: 'City name.', + example: 'Montreal', }, { - name: 'bytes', + name: 'geo.continent_name', level: 'core', - type: 'long', - format: 'bytes', - example: 184, - description: 'Bytes sent from the client to the server.', + type: 'keyword', + ignore_above: 1024, + description: 'Name of the continent.', + example: 'North America', }, { - name: 'packets', + name: 'geo.country_iso_code', level: 'core', - type: 'long', - example: 12, - description: 'Packets sent from the client to the server.', + type: 'keyword', + ignore_above: 1024, + description: 'Country ISO code.', + example: 'CA', }, { - name: 'geo', - title: 'Geo', - group: 2, - description: - 'Geo fields can carry data about a specific location related to an event or geo information derived from an IP field.', - type: 'group', - fields: [ - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - 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.country_name', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Country name.', + example: 'Canada', }, - ], - }, - { - name: 'cloud', - title: 'Cloud', - group: 2, - description: 'Fields related to the cloud or infrastructure the events are coming from.', - footnote: - 'Examples: If Metricbeat is running on an EC2 host and fetches data from its host, the cloud info contains the data about this machine. If Metricbeat runs on a remote machine outside the cloud and fetches data from a service running in the cloud, the field contains cloud data from the machine the service is running on.', - type: 'group', - fields: [ { - name: 'provider', + name: 'geo.location', + level: 'core', + type: 'geo_point', + description: 'Longitude and latitude.', + example: '{ "lon": -73.614830, "lat": 45.505918 }', + }, + { + name: 'geo.name', level: 'extended', - example: 'ec2', type: 'keyword', + ignore_above: 1024, description: - 'Name of the cloud provider. Example values are ec2, gce, or digitalocean.', + '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: 'availability_zone', - level: 'extended', - example: 'us-east-1c', + name: 'geo.region_iso_code', + level: 'core', type: 'keyword', - description: 'Availability zone in which this host is running.', + ignore_above: 1024, + description: 'Region ISO code.', + example: 'CA-QC', }, { - name: 'region', - level: 'extended', + name: 'geo.region_name', + level: 'core', type: 'keyword', - example: 'us-east-1', - description: 'Region in which this host is running.', + ignore_above: 1024, + description: 'Region name.', + example: 'Quebec', }, { - name: 'instance.id', - level: 'extended', - type: 'keyword', - example: 'i-1234567890abcdef0', - description: 'Instance ID of the host machine.', + name: 'ip', + level: 'core', + type: 'ip', + description: + 'IP address of the client.\n\nCan be one or multiple IPv4 or IPv6 addresses.', }, { - name: 'instance.name', - level: 'extended', + name: 'mac', + level: 'core', type: 'keyword', - description: 'Instance name of the host machine.', + ignore_above: 1024, + description: 'MAC address of the client.', }, { - name: 'machine.type', + name: 'nat.ip', level: 'extended', - type: 'keyword', - example: 't2.medium', - description: 'Machine type of the host machine.', + 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: 'account.id', + name: 'nat.port', level: 'extended', - type: 'keyword', - example: 666777888999, + type: 'long', + format: 'string', 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.', + 'Translated port of source based NAT sessions (e.g. internal client\nto internet).\n\nTypically connections traversing load balancers, firewalls, or routers.', }, - ], - }, - { - name: 'container', - title: 'Container', - group: 2, - description: - 'Container fields are used for meta information about the specific container that is the source of information. These fields help correlate data based containers from any runtime.', - type: 'group', - fields: [ { - name: 'runtime', - level: 'extended', - type: 'keyword', - description: 'Runtime managing this container.', - example: 'docker', + name: 'packets', + level: 'core', + type: 'long', + description: 'Packets sent from the client to the server.', + example: 12, }, { - name: 'id', + name: 'port', level: 'core', + type: 'long', + format: 'string', + description: 'Port of the client.', + }, + { + name: 'registered_domain', + level: 'extended', type: 'keyword', - description: 'Unique container id.', + 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: 'image.name', + name: 'top_level_domain', level: 'extended', type: 'keyword', - description: 'Name of the image the container was built on.', + 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: 'image.tag', + name: 'user.domain', level: 'extended', type: 'keyword', - description: 'Container image tag.', + 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: 'name', + name: 'user.email', level: 'extended', type: 'keyword', - description: 'Container name.', + ignore_above: 1024, + description: 'User email address.', }, { - name: 'labels', + name: 'user.full_name', level: 'extended', - type: 'object', - object_type: 'keyword', - description: 'Image labels.', + 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: 'destination', - title: 'Destination', - group: 2, - description: - 'Destination fields describe details about the destination of a packet/event. Destination fields are usually populated in conjunction with source fields.', - type: 'group', - fields: [ { - name: 'address', + name: 'user.group.domain', level: 'extended', type: 'keyword', + ignore_above: 1024, 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 of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', }, { - name: 'ip', - level: 'core', - type: 'ip', - description: - 'IP address of the destination. Can be one or multiple IPv4 or IPv6 addresses.', + name: 'user.group.id', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Unique identifier for the group on the system/platform.', }, { - name: 'port', - level: 'core', - type: 'long', - description: 'Port of the destination.', + name: 'user.group.name', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Name of the group.', }, { - name: 'mac', - level: 'core', + name: 'user.hash', + level: 'extended', type: 'keyword', - description: 'MAC address of the destination.', + 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: 'domain', + name: 'user.id', level: 'core', type: 'keyword', - description: 'Destination domain.', + ignore_above: 1024, + description: 'Unique identifiers of the user.', }, { - name: 'bytes', + name: 'user.name', level: 'core', - type: 'long', - format: 'bytes', - example: 184, - description: 'Bytes sent from the destination to the source.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - example: 12, - description: 'Packets sent from the destination to the source.', - }, - { - name: 'geo', - title: 'Geo', - group: 2, - description: - 'Geo fields can carry data about a specific location related to an event or geo information derived from an IP field.', - type: 'group', - fields: [ - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, + type: 'keyword', + ignore_above: 1024, + multi_fields: [ { - name: 'name', - level: 'extended', - type: 'keyword', - 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: 'text', + type: 'text', + norms: false, + default_field: false, }, ], + description: 'Short name or login of the user.', + example: 'albert', }, ], }, { - name: 'ecs', - title: 'ECS', + name: 'cloud', + title: 'Cloud', group: 2, - description: 'Meta-information specific to ECS.', + 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: 'version', - level: 'core', + name: 'account.id', + level: 'extended', type: 'keyword', - required: true, + ignore_above: 1024, 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. The current version is 1.0.0-beta2 .', - example: '1.0.0-beta2', + '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: 'error', - title: 'Error', - group: 2, - description: - 'These fields can represent errors of any kind. Use them for errors that happen while fetching events or in cases where the event itself contains an error.', - type: 'group', - fields: [ { - name: 'id', - level: 'core', + name: 'availability_zone', + level: 'extended', type: 'keyword', - description: 'Unique identifier for the error.', + ignore_above: 1024, + description: 'Availability zone in which this host is running.', + example: 'us-east-1c', }, { - name: 'message', - level: 'core', - type: 'text', - description: 'Error message.', + name: 'instance.id', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Instance ID of the host machine.', + example: 'i-1234567890abcdef0', }, { - name: 'code', - level: 'core', + name: 'instance.name', + level: 'extended', type: 'keyword', - description: 'Error code describing the error.', + ignore_above: 1024, + description: 'Instance name of the host machine.', }, - ], - }, - { - name: 'event', - title: 'Event', - group: 2, - description: - 'The event fields are used for context information about the log or metric event itself. A log is defined as an event containing details of something that happened. Log events must include the time at which the thing happened. Examples of log events include a process starting on a host, a network packet being sent from a source to a destination, or a network connection between a client and a server being initiated or closed. A metric is defined as an event containing one or more numerical or categorical measurements and the time at which the measurement was taken. Examples of metric events include memory pressure measured on a host, or vulnerabilities measured on a scanned host.', - type: 'group', - fields: [ { - name: 'id', - level: 'core', + name: 'machine.type', + level: 'extended', type: 'keyword', - description: 'Unique ID to describe the event.', - example: '8a4f500d', + ignore_above: 1024, + description: 'Machine type of the host machine.', + example: 't2.medium', }, { - name: 'kind', + name: 'provider', level: 'extended', type: 'keyword', + ignore_above: 1024, description: - 'The kind of the event. This gives information about what type of information the event contains, without being specific to the contents of the event. Examples are `event`, `state`, `alarm`. Warning: In future versions of ECS, we plan to provide a list of acceptable values for this field, please use with caution.', - example: 'state', + 'Name of the cloud provider. Example values are aws, azure, gcp,\nor digitalocean.', + example: 'aws', }, { - name: 'category', - level: 'core', + name: 'region', + level: 'extended', type: 'keyword', - description: - 'Event category. This contains high-level information about the contents of the event. It is more generic than `event.action`, in the sense that typically a category contains multiple actions. Warning: In future versions of ECS, we plan to provide a list of acceptable values for this field, please use with caution.', - example: 'user-management', + 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: 'action', + name: 'exists', level: 'core', - type: 'keyword', - 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', + type: 'boolean', + description: 'Boolean to capture if a signature is present.', + example: 'true', + default_field: false, }, { - name: 'outcome', + name: 'status', level: 'extended', type: 'keyword', + ignore_above: 1024, description: - 'The outcome of the event. If the event describes an action, this fields contains the outcome of that action. Examples outcomes are `success` and `failure`. Warning: In future versions of ECS, we plan to provide a list of acceptable values for this field, please use with caution.', - example: 'success', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - description: 'Reserved for future usage. Please avoid using this field for user data.', + '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: 'module', + name: 'subject_name', level: 'core', type: 'keyword', - description: - 'Name of the module this data is coming from. This information is coming from the modules used in Beats or Logstash.', - example: 'mysql', + ignore_above: 1024, + description: 'Subject name of the code signer', + example: 'Microsoft Corporation', + default_field: false, }, { - name: 'dataset', - level: 'core', - type: 'keyword', + name: 'trusted', + level: 'extended', + type: 'boolean', description: - 'Name of the dataset. The concept of a `dataset` (fileset / metricset) is used in Beats as a subset of modules. It contains the information which is currently stored in metricset.name and metricset.module or fileset.name.', - example: 'stats', + '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: 'severity', - level: 'core', - type: 'long', - example: '7', + name: 'valid', + level: 'extended', + type: 'boolean', description: - "Severity describes the severity of the event. What the different severity values mean can very different between use cases. It's up to the implementer to make sure severities are consistent across events. ", + '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: 'original', + name: 'id', level: 'core', type: 'keyword', - 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', - 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`.', - index: false, - doc_values: false, + ignore_above: 1024, + description: 'Unique container id.', }, { - name: 'hash', + name: 'image.name', level: 'extended', type: 'keyword', - example: '123456789012345678901234567890ABCD', - description: - 'Hash (perhaps logstash fingerprint) of raw field to be able to demonstrate log integrity.', - }, - { - name: 'duration', - level: 'core', - type: 'long', - format: 'duration', - input_format: 'nanoseconds', - 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.', + ignore_above: 1024, + description: 'Name of the image the container was built on.', }, { - name: 'timezone', + name: 'image.tag', level: 'extended', type: 'keyword', - 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: 'created', - level: 'core', - type: 'date', - description: - 'event.created contains the date when the event was created. This timestamp is distinct from @timestamp in that @timestamp contains the processed timestamp. For logs these two timestamps can be different as the timestamp in the log line and when the event is read for example by Filebeat are not identical. `@timestamp` must contain the timestamp extracted from the log line, event.created when the log line is read. The same could apply to package capturing where @timestamp contains the timestamp extracted from the network package and event.created when the event was created. In case the two timestamps are identical, @timestamp should be used.', + ignore_above: 1024, + description: 'Container image tags.', }, { - name: 'start', + name: 'labels', level: 'extended', - type: 'date', - description: - 'event.start contains the date when the event started or when the activity was first observed.', + type: 'object', + object_type: 'keyword', + description: 'Image labels.', }, { - name: 'end', + name: 'name', level: 'extended', - type: 'date', - description: - 'event.end contains the date when the event ended or when the activity was last observed.', - }, - { - name: 'risk_score', - level: 'core', - type: 'float', - description: - "Risk score or priority of the event (e.g. security solutions). Use your system's original value here. ", + type: 'keyword', + ignore_above: 1024, + description: 'Container name.', }, { - name: 'risk_score_norm', + name: 'runtime', level: 'extended', - type: 'float', - 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.', + type: 'keyword', + ignore_above: 1024, + description: 'Runtime managing this container.', + example: 'docker', }, ], }, { - name: 'file', + name: 'destination', + title: 'Destination', group: 2, - title: 'File', description: - 'A file is defined as a set of information that has been created on, or has existed on a filesystem. File objects can be associated with host events, network events, and/or file events (e.g., those produced by File Integrity Monitoring [FIM] products or services). File fields provide details about the affected file associated with the event or metric.', + '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: 'path', + name: 'address', level: 'extended', type: 'keyword', - description: 'Path to the file.', + 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: 'target_path', + name: 'as.number', level: 'extended', - type: 'keyword', - description: 'Target path for symlinks.', + 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: 'extension', + name: 'as.organization.name', level: 'extended', type: 'keyword', - description: 'File extension. This should allow easy filtering by file extensions.', - example: 'png', + ignore_above: 1024, + multi_fields: [ + { + name: 'text', + type: 'text', + norms: false, + default_field: false, + }, + ], + description: 'Organization name.', + example: 'Google LLC', }, { - name: 'type', - level: 'extended', - type: 'keyword', - description: 'File type (file, dir, or symlink).', + name: 'bytes', + level: 'core', + type: 'long', + format: 'bytes', + description: 'Bytes sent from the destination to the source.', + example: 184, }, { - name: 'device', - level: 'extended', + name: 'domain', + level: 'core', type: 'keyword', - description: 'Device that is the source of the file.', + ignore_above: 1024, + description: 'Destination domain.', }, { - name: 'inode', - level: 'extended', + name: 'geo.city_name', + level: 'core', type: 'keyword', - description: 'Inode representing the file in the filesystem.', + ignore_above: 1024, + description: 'City name.', + example: 'Montreal', }, { - name: 'uid', - level: 'extended', + name: 'geo.continent_name', + level: 'core', type: 'keyword', - description: 'The user ID (UID) or security identifier (SID) of the file owner.', + ignore_above: 1024, + description: 'Name of the continent.', + example: 'North America', }, { - name: 'owner', - level: 'extended', + name: 'geo.country_iso_code', + level: 'core', type: 'keyword', - description: "File owner's username.", + ignore_above: 1024, + description: 'Country ISO code.', + example: 'CA', }, { - name: 'gid', - level: 'extended', + name: 'geo.country_name', + level: 'core', type: 'keyword', - description: 'Primary group ID (GID) of the file.', + ignore_above: 1024, + description: 'Country name.', + example: 'Canada', }, { - name: 'group', - level: 'extended', - type: 'keyword', - description: 'Primary group name of the file.', - }, - { - name: 'mode', - level: 'extended', - type: 'keyword', - example: 416, - description: 'Mode of the file in octal representation.', - }, - { - name: 'size', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'File size in bytes (field is only added when `type` is `file`).', - }, - { - name: 'mtime', - level: 'extended', - type: 'date', - description: 'Last time file content was modified.', - }, - { - name: 'ctime', - level: 'extended', - type: 'date', - description: 'Last time file metadata changed.', - }, - ], - }, - { - name: 'group', - title: 'Group', - group: 2, - description: - 'The group fields are meant to represent groups that are relevant to the event.', - type: 'group', - fields: [ - { - name: 'id', - level: 'extended', - type: 'keyword', - description: 'Unique identifier for the group on the system/platform.', + name: 'geo.location', + level: 'core', + type: 'geo_point', + description: 'Longitude and latitude.', + example: '{ "lon": -73.614830, "lat": 45.505918 }', }, { - name: 'name', + name: 'geo.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. ECS host.* fields should be populated with details about the host on which the event happened, or on which the measurement was taken. Host types include hardware, virtual machines, Docker containers, and Kubernetes nodes.', - type: 'group', - fields: [ - { - name: 'hostname', - level: 'core', - type: 'keyword', + ignore_above: 1024, description: - 'Hostname of the host. It normally contains what the `hostname` command returns on the host machine.', + '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: 'name', + name: 'geo.region_iso_code', level: 'core', type: 'keyword', - 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.', + ignore_above: 1024, + description: 'Region ISO code.', + example: 'CA-QC', }, { - name: 'id', + name: 'geo.region_name', level: 'core', type: 'keyword', - 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`.', + ignore_above: 1024, + description: 'Region name.', + example: 'Quebec', }, { name: 'ip', level: 'core', type: 'ip', - description: 'Host ip address.', + description: + 'IP address of the destination.\n\nCan be one or multiple IPv4 or IPv6 addresses.', }, { name: 'mac', level: 'core', type: 'keyword', - description: 'Host mac address.', + ignore_above: 1024, + description: 'MAC address of the destination.', }, { - name: 'type', - level: 'core', - type: 'keyword', + name: 'nat.ip', + level: 'extended', + type: 'ip', 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.', + 'Translated ip of destination based NAT sessions (e.g. internet\nto private DMZ)\n\nTypically used with load balancers, firewalls, or routers.', }, { - name: 'architecture', - level: 'core', - type: 'keyword', - example: 'x86_64', - description: 'Operating system architecture.', + 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: 'os', - title: 'Operating System', - group: 2, - description: 'The OS fields contain information about the operating system.', - reusable: { - top_level: false, - expected: ['observer', 'host', 'user_agent'], - }, - type: 'group', - fields: [ - { - name: 'platform', - level: 'extended', - type: 'keyword', - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - example: 'Mac OS X', - description: 'Operating system name, without the version.', - }, - { - name: 'full', - level: 'extended', - type: 'keyword', - example: 'Mac OS Mojave', - description: 'Operating system name, including the version or code name.', - }, - { - name: 'family', - level: 'extended', - type: 'keyword', - example: 'debian', - description: 'OS family (such as redhat, debian, freebsd, windows).', - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - example: '10.14.1', - description: 'Operating system version as a raw string.', - }, - { - name: 'kernel', - level: 'extended', - type: 'keyword', - example: '4.4.0-112-generic', - description: 'Operating system kernel version as a raw string.', - }, - ], + name: 'packets', + level: 'core', + type: 'long', + description: 'Packets sent from the destination to the source.', + example: 12, }, { - name: 'geo', - title: 'Geo', - group: 2, - description: - 'Geo fields can carry data about a specific location related to an event or geo information derived from an IP field.', - type: 'group', - fields: [ - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - 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: 'port', + level: 'core', + type: 'long', + format: 'string', + description: 'Port of the destination.', }, - ], - }, - { - name: 'http', - title: 'HTTP', - group: 2, - description: 'Fields related to HTTP activity.', - type: 'group', - fields: [ { - name: 'request.method', + name: 'registered_domain', level: 'extended', type: 'keyword', + ignore_above: 1024, description: - 'Http request method. The field value must be normalized to lowercase for querying. See "Lowercase Capitalization" in the "Implementing ECS" section.', - example: 'get, post, put', + '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: 'request.body.content', + name: 'top_level_domain', level: 'extended', type: 'keyword', - description: 'The full http request body.', - example: 'Hello world', + 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: 'request.referrer', + name: 'user.domain', level: 'extended', type: 'keyword', - description: 'Referrer for this HTTP request.', - example: 'https://blog.example.com/', - }, - { - name: 'response.status_code', - level: 'extended', - type: 'long', - description: 'Http response status code.', - example: 404, + 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: 'response.body.content', + name: 'user.email', level: 'extended', type: 'keyword', - description: 'The full http response body.', - example: 'Hello world', + ignore_above: 1024, + description: 'User email address.', }, { - name: 'version', + name: 'user.full_name', level: 'extended', type: 'keyword', - description: 'Http version.', - example: 1.1, + 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: 'request.bytes', + name: 'user.group.domain', level: 'extended', - type: 'long', - format: 'bytes', - description: 'Total size in bytes of the request (body and headers).', - example: 1437, + 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: 'request.body.bytes', + name: 'user.group.id', level: 'extended', - type: 'long', - format: 'bytes', - description: 'Size in bytes of the request body.', - example: 887, + type: 'keyword', + ignore_above: 1024, + description: 'Unique identifier for the group on the system/platform.', }, { - name: 'response.bytes', + name: 'user.group.name', level: 'extended', - type: 'long', - format: 'bytes', - description: 'Total size in bytes of the response (body and headers).', - example: 1437, + type: 'keyword', + ignore_above: 1024, + description: 'Name of the group.', }, { - name: 'response.body.bytes', + name: 'user.hash', level: 'extended', - type: 'long', - format: 'bytes', - description: 'Size in bytes of the response body.', - example: 887, + 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: 'log', - title: 'Log', - description: 'Fields which are specific to log events.', - type: 'group', - fields: [ { - name: 'level', + name: 'user.id', level: 'core', type: 'keyword', - description: 'Log level of the log event. Some examples are `WARN`, `ERR`, `INFO`.', - example: 'ERR', + ignore_above: 1024, + description: 'Unique identifiers of the user.', }, { - name: 'original', + name: 'user.name', level: 'core', type: 'keyword', - example: 'Sep 19 08:26:10 localhost My log', - index: false, - doc_values: false, - 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`. ", + 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: 'network', - title: 'Network', + name: 'dll', + title: 'DLL', group: 2, description: - 'The network is defined as the communication path over which a host or network event happens. The network.* fields should be populated with details about the network activity associated with an event.', + '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: 'name', + 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', - description: 'Name given by operators to sections of their network.', - example: 'Guest Wifi', + 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: 'type', + 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: - '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 "Lowercase Capitalization" in the "Implementing ECS" section.', - example: 'ipv4', + '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: 'iana_number', + name: 'code_signature.valid', level: 'extended', - type: 'keyword', + type: 'boolean', 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, + '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: 'transport', - level: 'core', + name: 'hash.md5', + level: 'extended', type: 'keyword', - 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 "Lowercase Capitalization" in the "Implementing ECS" section.', - example: 'tcp', + ignore_above: 1024, + description: 'MD5 hash.', + default_field: false, }, { - name: 'application', + name: 'hash.sha1', level: 'extended', type: 'keyword', - description: - 'A name given to an application. 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 "Lowercase Capitalization" in the "Implementing ECS" section.', - example: 'aim', + ignore_above: 1024, + description: 'SHA1 hash.', + default_field: false, }, { - name: 'protocol', - level: 'core', + name: 'hash.sha256', + level: 'extended', type: 'keyword', - description: - 'L7 Network protocol name. ex. http, lumberjack, transport protocol. The field value must be normalized to lowercase for querying. See "Lowercase Capitalization" in the "Implementing ECS" section.', - example: 'http', + ignore_above: 1024, + description: 'SHA256 hash.', + default_field: false, }, { - name: 'direction', + 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: - "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 of the library.\n\nThis generally maps to the name of the file on disk.', + example: 'kernel32.dll', + default_field: false, }, { - 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: 'community_id', + name: 'path', level: 'extended', type: 'keyword', - 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: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: - 'Total bytes transferred in both directions. If `source.bytes` and `destination.bytes` are known, `network.bytes` is their sum.', - example: 368, - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: - 'Total packets transferred in both directions. If `source.packets` and `destination.packets` are known, `network.packets` is their sum.', - example: 24, - }, - ], - }, - { - name: 'observer', - title: 'Observer', - group: 2, - description: - 'An observer is defined as a special network, security, or application device used to detect, observe, or create network, security, or application-related events and metrics. This could be a custom hardware appliance or a server that has been configured to run special network, security, or application software. Examples include firewalls, intrusion detection/prevention systems, network monitoring sensors, web application firewalls, data loss prevention systems, and APM servers. The observer.* fields shall be populated with details of the system, if any, that detects, observes and/or creates a network, security, or application event or metric. Message queues and ETL components used in processing events or metrics are not considered observers in ECS.', - type: 'group', - fields: [ - { - name: 'mac', - level: 'core', - type: 'keyword', - description: 'MAC address of the observer', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: 'IP address of the observer.', + ignore_above: 1024, + description: 'Full file path of the library.', + example: 'C:\\Windows\\System32\\kernel32.dll', + default_field: false, }, { - name: 'hostname', - level: 'core', + name: 'pe.company', + level: 'extended', type: 'keyword', - description: 'Hostname of the observer.', + ignore_above: 1024, + description: 'Internal company name of the file, provided at compile-time.', + example: 'Microsoft Corporation', + default_field: false, }, { - name: 'vendor', - level: 'core', + name: 'pe.description', + level: 'extended', type: 'keyword', - description: 'observer vendor information.', + ignore_above: 1024, + description: 'Internal description of the file, provided at compile-time.', + example: 'Paint', + default_field: false, }, { - name: 'version', - level: 'core', + name: 'pe.file_version', + level: 'extended', type: 'keyword', - description: 'Observer version.', + ignore_above: 1024, + description: 'Internal version of the file, provided at compile-time.', + example: '6.3.9600.17415', + default_field: false, }, { - name: 'serial_number', + name: 'pe.original_file_name', level: 'extended', type: 'keyword', - description: 'Observer serial number.', + ignore_above: 1024, + description: 'Internal name of the file, provided at compile-time.', + example: 'MSPAINT.EXE', + default_field: false, }, { - name: 'type', - level: 'core', + name: 'pe.product', + level: 'extended', type: 'keyword', - 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: 'os', - title: 'Operating System', - group: 2, - description: 'The OS fields contain information about the operating system.', - reusable: { - top_level: false, - expected: ['observer', 'host', 'user_agent'], - }, - type: 'group', - fields: [ - { - name: 'platform', - level: 'extended', - type: 'keyword', - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - example: 'Mac OS X', - description: 'Operating system name, without the version.', - }, - { - name: 'full', - level: 'extended', - type: 'keyword', - example: 'Mac OS Mojave', - description: 'Operating system name, including the version or code name.', - }, - { - name: 'family', - level: 'extended', - type: 'keyword', - example: 'debian', - description: 'OS family (such as redhat, debian, freebsd, windows).', - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - example: '10.14.1', - description: 'Operating system version as a raw string.', - }, - { - name: 'kernel', - level: 'extended', - type: 'keyword', - example: '4.4.0-112-generic', - description: 'Operating system kernel version as a raw string.', - }, - ], - }, - { - name: 'geo', - title: 'Geo', - group: 2, - description: - 'Geo fields can carry data about a specific location related to an event or geo information derived from an IP field.', - type: 'group', - fields: [ - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - 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', - }, - ], + ignore_above: 1024, + description: 'Internal product name of the file, provided at compile-time.', + example: 'Microsoft® Windows® Operating System', + default_field: false, }, ], }, { - name: 'organization', - title: 'Organization', + name: 'dns', + title: 'DNS', group: 2, description: - 'The organization fields enrich data with information about the company or entity the data is associated with. These fields help you arrange or filter data stored in an index by one or multiple organizations.', + '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: 'name', + name: 'answers', level: 'extended', - type: 'keyword', - description: 'Organization name.', + 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: 'id', + name: 'answers.class', level: 'extended', type: 'keyword', - description: 'Unique identifier for the organization.', + ignore_above: 1024, + description: 'The class of DNS data contained in this resource record.', + example: 'IN', }, - ], - }, - { - name: 'os', - title: 'Operating System', - group: 2, - description: 'The OS fields contain information about the operating system.', - reusable: { - top_level: false, - expected: ['observer', 'host', 'user_agent'], - }, - type: 'group', - fields: [ { - name: 'platform', + name: 'answers.data', level: 'extended', type: 'keyword', - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', + 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: 'name', + name: 'answers.name', level: 'extended', type: 'keyword', - example: 'Mac OS X', - description: 'Operating system name, without the version.', + 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: 'full', + name: 'answers.ttl', level: 'extended', - type: 'keyword', - example: 'Mac OS Mojave', - description: 'Operating system name, including the version or code name.', + 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: 'family', + name: 'answers.type', level: 'extended', type: 'keyword', - example: 'debian', - description: 'OS family (such as redhat, debian, freebsd, windows).', + ignore_above: 1024, + description: 'The type of data contained in this resource record.', + example: 'CNAME', }, { - name: 'version', + name: 'header_flags', level: 'extended', type: 'keyword', - example: '10.14.1', - description: 'Operating system version as a raw string.', + 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: 'kernel', + name: 'id', level: 'extended', type: 'keyword', - example: '4.4.0-112-generic', - description: 'Operating system kernel version as a raw string.', + 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: 'process', - title: 'Process', - group: 2, - description: - 'These fields contain information about a process. These fields can help you correlate metrics information with a process id/name from a log message. The `process.pid` often stays in the metric itself and is copied to the global field for correlation.', - type: 'group', - fields: [ { - name: 'pid', - level: 'core', - type: 'long', - description: 'Process id.', - example: 'ssh', + 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: 'name', + name: 'question.class', level: 'extended', type: 'keyword', - description: 'Process name. Sometimes called program name or similar.', - example: 'ssh', + ignore_above: 1024, + description: 'The class of records being queried.', + example: 'IN', }, { - name: 'ppid', + name: 'question.name', level: 'extended', - type: 'long', - description: 'Process parent id.', + 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: 'args', + name: 'question.registered_domain', level: 'extended', type: 'keyword', - description: 'Process arguments. May be filtered to protect sensitive information.', - example: ['ssh', '-l', 'user', '10.0.0.16'], + 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: 'executable', + name: 'question.subdomain', level: 'extended', type: 'keyword', - description: 'Absolute path to the process executable.', - example: '/usr/bin/ssh', + 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: 'title', + name: 'question.top_level_domain', level: 'extended', type: 'keyword', + ignore_above: 1024, 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.', + '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: 'thread.id', + name: 'question.type', level: 'extended', - type: 'long', - example: 4242, - description: 'Thread ID.', + type: 'keyword', + ignore_above: 1024, + description: 'The type of record being queried.', + example: 'AAAA', }, { - name: 'start', + name: 'resolved_ip', level: 'extended', - type: 'date', - example: '2016-05-23T08:05:34.853Z', - description: 'The time the process started.', + 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: 'working_directory', + name: 'response_code', level: 'extended', type: 'keyword', - example: '/home/alice', - description: 'The working directory of the process.', + 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: 'related', - title: 'Related', + name: 'ecs', + title: 'ECS', group: 2, - description: - 'This field set is meant to facilitate pivoting around a piece of data. Some pieces of information can be seen in many places in ECS. To facilitate searching for them, append values to their corresponding field in `related.`. A concrete example is IP addresses, which can be under host, observer, source, destination, client, server, and network.forwarded_ip. If you append all IPs to `related.ip`, you can then search for a given IP trivially, no matter where it appeared, by querying `related.ip:a.b.c.d`.', + description: 'Meta-information specific to ECS.', type: 'group', fields: [ { - name: 'ip', - level: 'extended', - type: 'ip', - description: 'All of the IPs seen on your event.', + 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: 'server', - title: 'Server', + name: 'error', + title: 'Error', group: 2, description: - 'A Server is defined as the responder in a network connection for events regarding sessions, connections, or bidirectional flow records. For TCP events, the server is the receiver of the initial SYN packet(s) of the TCP connection. For other protocols, the server is generally the responder in the network transaction. Some systems actually use the term "responder" to refer the server in TCP connections. The server fields describe details about the system acting as the server in the network event. Server fields are usually populated in conjunction with client fields. Server fields are generally not populated for packet-level events. Client / server representations can add semantic context to an exchange, which is helpful to visualize the data in certain situations. If your context falls in that category, you should still ensure that source and destination are filled appropriately.', + '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: 'address', - level: 'extended', - type: 'keyword', - 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: 'ip', - level: 'core', - type: 'ip', - description: 'IP address of the server. Can be one or multiple IPv4 or IPv6 addresses.', - }, - { - name: 'port', - level: 'core', - type: 'long', - description: 'Port of the server.', - }, - { - name: 'mac', + name: 'code', level: 'core', type: 'keyword', - description: 'MAC address of the server.', + ignore_above: 1024, + description: 'Error code describing the error.', }, { - name: 'domain', + name: 'id', level: 'core', type: 'keyword', - description: 'Server domain.', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - example: 184, - description: 'Bytes sent from the server to the client.', + ignore_above: 1024, + description: 'Unique identifier for the error.', }, { - name: 'packets', + name: 'message', level: 'core', - type: 'long', - example: 12, - description: 'Packets sent from the server to the client.', + type: 'text', + description: 'Error message.', }, { - name: 'geo', - title: 'Geo', - group: 2, - description: - 'Geo fields can carry data about a specific location related to an event or geo information derived from an IP field.', - type: 'group', - fields: [ - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, + name: 'stack_trace', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + multi_fields: [ { - name: 'name', - level: 'extended', - type: 'keyword', - 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: '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: 'service', - title: 'Service', + name: 'event', + title: 'Event', group: 2, description: - 'The service fields describe the service for or from which the data was collected. These fields help you find and correlate logs for a specific service and version.', + '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: 'id', + name: 'action', level: 'core', type: 'keyword', + ignore_above: 1024, description: - 'Unique identifier of the running service. This id should uniquely identify this service. This makes it possible to correlate logs and metrics for one specific service. Example: If you are experiencing issues with one redis instance, you can filter on that id to see metrics and logs for that single instance.', - example: 'd37e5ebfe0ae6c4972dbe9f0174a1637bb8247f6', + '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: 'name', + name: 'category', level: 'core', type: 'keyword', - example: 'elasticsearch-metrics', + ignore_above: 1024, description: - 'Name of the service data is collected from. The name of the service is normally user given. This allows if two instances of the same service are running on the same machine they can be differentiated by the `service.name`. Also it 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.', + '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: 'type', - level: 'core', + name: 'code', + level: 'extended', type: 'keyword', - example: 'elasticsearch', + ignore_above: 1024, 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`.', + '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: 'state', + name: 'created', level: 'core', - type: 'keyword', - description: 'Current state of the service.', + 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: 'version', + name: 'dataset', level: 'core', type: 'keyword', - example: '3.2.4', + ignore_above: 1024, 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.', + '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: 'ephemeral_id', + 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: 'keyword', + type: 'date', description: - 'Ephemeral identifier of this service (if one exists). This id normally changes across restarts, but `service.id` does not.', - example: '8a4f500f', + 'event.end contains the date when the event ended or when the activity\nwas last observed.', }, - ], - }, - { - name: 'source', - title: 'Source', - group: 2, - description: - 'Source fields describe details about the source of a packet/event. Source fields are usually populated in conjunction with destination fields.', - type: 'group', - fields: [ { - name: 'address', + name: 'hash', level: 'extended', type: 'keyword', + ignore_above: 1024, 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.', + 'Hash (perhaps logstash fingerprint) of raw field to be able to\ndemonstrate log integrity.', + example: '123456789012345678901234567890ABCD', }, { - name: 'ip', + name: 'id', level: 'core', - type: 'ip', - description: 'IP address of the source. Can be one or multiple IPv4 or IPv6 addresses.', + type: 'keyword', + ignore_above: 1024, + description: 'Unique ID to describe the event.', + example: '8a4f500d', }, { - name: 'port', + name: 'ingested', level: 'core', - type: 'long', - description: 'Port of the source.', + 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: 'mac', + name: 'kind', level: 'core', type: 'keyword', - description: 'MAC address of the source.', + 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: 'domain', + name: 'module', level: 'core', type: 'keyword', - description: 'Source domain.', + 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: 'bytes', + name: 'original', level: 'core', - type: 'long', - format: 'bytes', - example: 184, - description: 'Bytes sent from the source to the destination.', + 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: 'packets', + name: 'outcome', level: 'core', - type: 'long', - example: 12, - description: 'Packets sent from the source to the destination.', - }, - { - name: 'geo', - title: 'Geo', - group: 2, + type: 'keyword', + ignore_above: 1024, description: - 'Geo fields can carry data about a specific location related to an event or geo information derived from an IP field.', - type: 'group', - fields: [ - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - 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', - }, - ], + '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: 'url', - title: 'URL', - description: 'URL fields provide a complete URL, with scheme, host, and path.', - type: 'group', - fields: [ { - name: 'original', + name: 'provider', level: 'extended', type: 'keyword', + ignore_above: 1024, 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', + '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: 'full', + name: 'reference', level: 'extended', type: 'keyword', + ignore_above: 1024, 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', + '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: 'scheme', - level: 'extended', - type: 'keyword', + name: 'risk_score', + level: 'core', + type: 'float', description: - 'Scheme of the request, such as "https". Note: The `:` is not part of the scheme.', - example: 'https', + "Risk score or priority of the event (e.g. security solutions).\nUse your system's original value here.", }, { - name: 'domain', + name: 'risk_score_norm', level: 'extended', - type: 'keyword', + type: 'float', description: - 'Domain of the request, 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', + '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: 'port', + name: 'sequence', level: 'extended', - type: 'integer', - description: 'Port of the request, such as 443.', - example: 443, + 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: 'path', - level: 'extended', - type: 'keyword', - description: 'Path of the request, such as "/search".', + 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: 'query', + name: 'start', level: 'extended', - type: 'keyword', + type: 'date', 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.', + 'event.start contains the date when the event started or when the\nactivity was first observed.', }, { - name: 'fragment', + name: 'timezone', level: 'extended', type: 'keyword', + ignore_above: 1024, description: - 'Portion of the url after the `#`, such as "top". The `#` is not part of the fragment.', + '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: 'username', - level: 'extended', + name: 'type', + level: 'core', type: 'keyword', - description: 'Username of the request.', + 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: 'password', + name: 'url', level: 'extended', type: 'keyword', - description: 'Password of the request.', + 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: 'user', - title: 'User', + name: 'file', + title: 'File', group: 2, description: - 'The user fields describe information about the user that is relevant to the event. Fields can have one entry or multiple entries. If a user has more than one id, provide an array that includes all of them.', - reusable: { - top_level: true, - expected: ['client', 'destination', 'host', 'server', 'source'], - }, + '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: 'id', - level: 'core', + 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', - description: 'One or multiple unique identifiers of the user.', + 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: 'name', + name: 'code_signature.exists', level: 'core', - type: 'keyword', - example: 'albert', - description: 'Short name or login of the user.', + type: 'boolean', + description: 'Boolean to capture if a signature is present.', + example: 'true', + default_field: false, }, { - name: 'full_name', + name: 'code_signature.status', level: 'extended', type: 'keyword', - example: 'Albert Einstein', - description: "User's full name, if available. ", + 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: 'email', - level: 'extended', + name: 'code_signature.subject_name', + level: 'core', type: 'keyword', - description: 'User email address.', + ignore_above: 1024, + description: 'Subject name of the code signer', + example: 'Microsoft Corporation', + default_field: false, }, { - name: 'hash', + name: 'code_signature.trusted', level: 'extended', - type: 'keyword', + type: 'boolean', 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.', + '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: 'group', - title: 'Group', - group: 2, + name: 'code_signature.valid', + level: 'extended', + type: 'boolean', description: - 'The group fields are meant to represent groups that are relevant to 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.', - }, - ], + '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: 'user_agent', - title: 'User agent', - group: 2, - description: - 'The user_agent fields normally come from a browser request. They often show up in web service logs coming from the parsed user agent string.', - type: 'group', - fields: [ { - name: 'original', + name: 'created', 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 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1', + type: 'date', + description: + 'File creation time.\n\nNote that not all filesystems store the creation time.', }, { - name: 'name', + name: 'ctime', level: 'extended', - type: 'keyword', - example: 'Safari', - description: 'Name of the user agent.', + 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: 'version', + name: 'device', level: 'extended', type: 'keyword', - description: 'Version of the user agent.', - example: 12, + ignore_above: 1024, + description: 'Device that is the source of the file.', + example: 'sda', }, { - name: 'device.name', + name: 'directory', level: 'extended', type: 'keyword', - example: 'iPhone', - description: 'Name of the device.', + ignore_above: 1024, + description: + 'Directory where the file is located. It should include the drive\nletter, when appropriate.', + example: '/home/alice', }, { - name: 'os', - title: 'Operating System', - group: 2, - description: 'The OS fields contain information about the operating system.', - reusable: { - top_level: false, - expected: ['observer', 'host', 'user_agent'], - }, - type: 'group', - fields: [ - { - name: 'platform', - level: 'extended', - type: 'keyword', - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - example: 'Mac OS X', - description: 'Operating system name, without the version.', - }, - { - name: 'full', - level: 'extended', - type: 'keyword', - example: 'Mac OS Mojave', - description: 'Operating system name, including the version or code name.', - }, - { - name: 'family', - level: 'extended', - type: 'keyword', - example: 'debian', - description: 'OS family (such as redhat, debian, freebsd, windows).', - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - example: '10.14.1', - description: 'Operating system version as a raw string.', - }, - { - name: 'kernel', - level: 'extended', - type: 'keyword', - example: '4.4.0-112-generic', - description: 'Operating system kernel version as a raw string.', - }, - ], + 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: 'agent.hostname', - type: 'keyword', - description: 'Hostname of the agent.', - }, - ], - }, - { - key: 'beat', - title: 'Beat', - description: 'Contains common beat fields available in all event types.', - fields: [ - { - name: 'beat.timezone', - type: 'alias', - path: 'event.timezone', - migration: true, - }, - { - name: 'fields', - type: 'object', - object_type: 'keyword', - description: 'Contains user configurable fields.', - }, - { - name: 'error', - type: 'group', - description: 'Error fields containing additional info in case of errors.', - fields: [ { - name: 'type', + name: 'extension', + level: 'extended', type: 'keyword', - description: 'Error type.', + ignore_above: 1024, + description: 'File extension.', + example: 'png', }, - ], - }, - { - name: 'beat.name', - type: 'alias', - path: 'host.name', - migration: true, - }, - { - name: 'beat.hostname', - type: 'alias', - path: 'agent.hostname', - migration: true, - }, - ], - }, - { - key: 'cloud', - title: 'Cloud provider metadata', - description: 'Metadata from cloud providers added by the add_cloud_metadata processor.', - fields: [ - { - name: 'cloud.project.id', - example: 'project-x', - description: 'Name of the project in Google Cloud.', - }, - { - 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.', - short_config: false, - anchor: 'docker-processor', - fields: [ - { - name: 'docker', - type: 'group', - fields: [ { - name: 'container.id', - type: 'alias', - path: 'container.id', - migration: true, + name: 'gid', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Primary group ID (GID) of the file.', + example: '1001', }, { - name: 'container.image', - type: 'alias', - path: 'container.image.name', - migration: true, + name: 'group', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Primary group name of the file.', + example: 'alice', }, { - name: 'container.name', - type: 'alias', - path: 'container.name', - migration: true, + name: 'hash.md5', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'MD5 hash.', }, { - name: 'container.labels', - type: 'object', - object_type: 'keyword', - description: 'Image labels.', + name: 'hash.sha1', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'SHA1 hash.', }, - ], - }, - ], - }, - { - key: 'host', - title: 'Host', - description: 'Info collected for the host machine.', - anchor: 'host-processor', - }, - { - key: 'kubernetes', - title: 'Kubernetes', - description: 'Kubernetes metadata added by the kubernetes processor', - short_config: false, - anchor: 'kubernetes-processor', - fields: [ - { - name: 'kubernetes', - type: 'group', - fields: [ { - name: 'pod.name', + name: 'hash.sha256', + level: 'extended', type: 'keyword', - description: 'Kubernetes pod name', + ignore_above: 1024, + description: 'SHA256 hash.', }, { - name: 'pod.uid', + name: 'hash.sha512', + level: 'extended', type: 'keyword', - description: 'Kubernetes Pod UID', + ignore_above: 1024, + description: 'SHA512 hash.', }, { - name: 'namespace', + name: 'inode', + level: 'extended', type: 'keyword', - description: 'Kubernetes namespace', + ignore_above: 1024, + description: 'Inode representing the file in the filesystem.', + example: '256383', }, { - name: 'node.name', + name: 'mime_type', + level: 'extended', type: 'keyword', - description: 'Kubernetes node name', + 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: 'labels', - type: 'object', - description: 'Kubernetes labels map', + name: 'mode', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Mode of the file in octal representation.', + example: '0640', }, { - name: 'annotations', - type: 'object', - description: 'Kubernetes annotations map', + name: 'mtime', + level: 'extended', + type: 'date', + description: 'Last time the file content was modified.', }, { - name: 'container.name', + name: 'name', + level: 'extended', type: 'keyword', - description: 'Kubernetes container name', + ignore_above: 1024, + description: 'Name of the file including the extension, without the directory.', + example: 'example.png', }, { - name: 'container.image', + name: 'owner', + level: 'extended', type: 'keyword', - description: 'Kubernetes container image', - }, - ], - }, - ], - }, - { - key: 'process', - title: 'Process', - description: 'Process metadata fields', - fields: [ - { - name: 'process', - type: 'group', - fields: [ - { - name: 'exe', - type: 'alias', - path: 'process.executable', - migration: true, - }, - ], - }, - ], - }, - { - key: 'common', - title: 'Common', - description: - 'These fields contain data about the environment in which the transaction or flow was captured.', - fields: [ - { - name: 'type', - description: - 'The type of the transaction (for example, HTTP, MySQL, Redis, or RUM) or "flow" in case of flows.', - required: true, - }, - { - name: 'server.process.name', - description: 'The name of the process that served the transaction.', - }, - { - name: 'server.process.args', - description: 'The command-line of the process that served the transaction.', - }, - { - name: 'server.process.executable', - description: 'Absolute path to the server process executable.', - }, - { - name: 'server.process.working_directory', - description: 'The working directory of the server process.', - }, - { - name: 'server.process.start', - description: 'The time the server process started.', - }, - { - name: 'client.process.name', - description: 'The name of the process that initiated the transaction.', - }, - { - name: 'client.process.args', - description: 'The command-line of the process that initiated the transaction.', - }, - { - name: 'client.process.executable', - description: 'Absolute path to the client process executable.', - }, - { - name: 'client.process.working_directory', - description: 'The working directory of the client process.', - }, - { - name: 'client.process.start', - description: 'The time the client process started.', - }, - { - 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`. Unless this field is disabled, it always has a value, and it matches the `client_ip` for non proxy clients.', - }, - { - name: 'transport', - type: 'alias', - path: 'network.transport', - migration: true, - description: - 'The transport protocol used for the transaction. If not specified, then tcp is assumed.', - }, - ], - }, - { - key: 'flows_event', - title: 'Flow Event', - description: 'These fields contain data about the flow itself.', - 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.', - }, - { - name: 'flow.id', - description: 'Internal flow ID based on connection meta data and address.', - }, - { - 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. ", - }, - { - 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.', - 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.', - 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).', - }, - { - 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.', - }, - { - 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.', - }, - { - 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`.', - }, - { - 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.', - }, - { - 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.', - }, - ], - }, - { - 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.', - }, - { - 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.', - }, - ], - }, - { - key: 'trans_measurements', - title: 'Measurements (Transactions)', - description: 'These fields contain measurements related to the transaction.', - 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.', - }, - { - 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.', - }, - ], - }, - { - 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', - example: 404, + ignore_above: 1024, + description: "File owner's username.", + example: 'alice', }, { - name: 'reply-text', + name: 'path', + level: 'extended', type: 'keyword', - description: 'Text explaining the error.', - }, - { - name: 'class-id', - type: 'long', - description: 'Failing method class.', + 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: 'method-id', - type: 'long', - description: 'Failing method ID.', + 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: 'exchange', + name: 'pe.description', + level: 'extended', type: 'keyword', - description: 'Name of the exchange.', + ignore_above: 1024, + description: 'Internal description of the file, provided at compile-time.', + example: 'Paint', + default_field: false, }, { - name: 'exchange-type', + name: 'pe.file_version', + level: 'extended', type: 'keyword', - description: 'Exchange type.', - example: 'fanout', + ignore_above: 1024, + description: 'Internal version of the file, provided at compile-time.', + example: '6.3.9600.17415', + default_field: false, }, { - name: 'passive', - type: 'boolean', - description: 'If set, do not create exchange/queue.', + 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: 'durable', - type: 'boolean', - description: 'If set, request a durable exchange/queue.', + 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: 'exclusive', - type: 'boolean', - description: 'If set, request an exclusive queue.', + name: 'size', + level: 'extended', + type: 'long', + description: 'File size in bytes.\n\nOnly relevant when `file.type` is "file".', + example: 16384, }, { - name: 'auto-delete', - type: 'boolean', - description: 'If set, auto-delete queue when unused.', + 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: 'no-wait', - type: 'boolean', - description: 'If set, the server will not respond to the method.', + name: 'type', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'File type (file, dir, or symlink).', + example: 'file', }, { - name: 'consumer-tag', - description: 'Identifier for the consumer, valid within the current channel.', + 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: 'delivery-tag', - type: 'long', - description: 'The server-assigned and channel-specific delivery tag.', + name: 'city_name', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'City name.', + example: 'Montreal', }, { - name: 'message-count', - type: 'long', - description: - 'The number of messages in the queue, which will be zero for newly-declared queues.', + name: 'continent_name', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Name of the continent.', + example: 'North America', }, { - name: 'consumer-count', - type: 'long', - description: 'The number of consumers of a queue.', + name: 'country_iso_code', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Country ISO code.', + example: 'CA', }, { - name: 'routing-key', + name: 'country_name', + level: 'core', type: 'keyword', - description: 'Message routing key.', + ignore_above: 1024, + description: 'Country name.', + example: 'Canada', }, { - name: 'no-ack', - type: 'boolean', - description: 'If set, the server does not expect acknowledgements for messages.', + name: 'location', + level: 'core', + type: 'geo_point', + description: 'Longitude and latitude.', + example: '{ "lon": -73.614830, "lat": 45.505918 }', }, { - name: 'no-local', - type: 'boolean', + name: 'name', + level: 'extended', + type: 'keyword', + ignore_above: 1024, description: - 'If set, the server will not send messages to the connection that published them.', + '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: 'if-unused', - type: 'boolean', - description: 'Delete only if unused.', + name: 'region_iso_code', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Region ISO code.', + example: 'CA-QC', }, { - name: 'if-empty', - type: 'boolean', - description: 'Delete only if empty.', + 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: 'queue', + name: 'domain', + level: 'extended', type: 'keyword', - description: 'The queue name identifies the queue within the vhost.', - }, - { - name: 'redelivered', - type: 'boolean', - description: - 'Indicates that the message has been previously delivered to this or another client.', - }, - { - name: 'multiple', - type: 'boolean', - description: 'Acknowledge multiple messages.', - }, - { - name: 'arguments', - type: 'object', + ignore_above: 1024, description: - 'Optional additional arguments passed to some methods. Can be of various types.', - }, - { - name: 'mandatory', - type: 'boolean', - description: 'Indicates mandatory routing.', + 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', }, { - name: 'immediate', - type: 'boolean', - description: 'Request immediate delivery.', - }, - { - name: 'content-type', + name: 'id', + level: 'extended', type: 'keyword', - description: 'MIME content type.', - example: 'text/plain', + ignore_above: 1024, + description: 'Unique identifier for the group on the system/platform.', }, { - name: 'content-encoding', + name: 'name', + level: 'extended', type: 'keyword', - description: 'MIME content encoding.', - }, - { - name: 'headers', - type: 'object', - object_type: 'keyword', - description: 'Message header field table.', + 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: 'delivery-mode', + name: 'md5', + level: 'extended', type: 'keyword', - description: 'Non-persistent (1) or persistent (2).', - }, - { - name: 'priority', - type: 'long', - description: 'Message priority, 0 to 9.', + ignore_above: 1024, + description: 'MD5 hash.', }, { - name: 'correlation-id', + name: 'sha1', + level: 'extended', type: 'keyword', - description: 'Application correlation identifier.', + ignore_above: 1024, + description: 'SHA1 hash.', }, { - name: 'reply-to', + name: 'sha256', + level: 'extended', type: 'keyword', - description: 'Address to reply to.', + ignore_above: 1024, + description: 'SHA256 hash.', }, { - name: 'expiration', + name: 'sha512', + level: 'extended', type: 'keyword', - description: 'Message expiration specification.', + 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: 'message-id', + name: 'architecture', + level: 'core', type: 'keyword', - description: 'Application message identifier.', + ignore_above: 1024, + description: 'Operating system architecture.', + example: 'x86_64', }, { - name: 'timestamp', + name: 'domain', + level: 'extended', type: 'keyword', - description: 'Message timestamp.', + 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: 'type', + name: 'geo.city_name', + level: 'core', type: 'keyword', - description: 'Message type name.', + ignore_above: 1024, + description: 'City name.', + example: 'Montreal', }, { - name: 'user-id', + name: 'geo.continent_name', + level: 'core', type: 'keyword', - description: 'Creating user id.', + ignore_above: 1024, + description: 'Name of the continent.', + example: 'North America', }, { - name: 'app-id', + name: 'geo.country_iso_code', + level: 'core', type: 'keyword', - description: 'Creating application id.', - }, - ], - }, - ], - }, - { - 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.', + ignore_above: 1024, + description: 'Country ISO code.', + example: 'CA', }, { - 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: 'geo.country_name', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Country name.', + example: 'Canada', }, { - 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.', - }, - ], - }, - ], - }, - ], + name: 'geo.location', + level: 'core', + type: 'geo_point', + description: 'Longitude and latitude.', + example: '{ "lon": -73.614830, "lat": 45.505918 }', }, - ], - }, - ], - }, - { - key: 'dhcpv4', - title: 'DHCPv4', - description: 'DHCPv4 event fields', - fields: [ - { - name: 'dhcpv4', - type: 'group', - fields: [ { - name: 'transaction_id', + name: 'geo.name', + level: 'extended', type: 'keyword', + ignore_above: 1024, 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: 'seconds', - type: 'long', - description: - 'Number of seconds elapsed since client began address acquisition or renewal process.', + '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: 'flags', + name: 'geo.region_iso_code', + level: 'core', type: 'keyword', - description: - 'Flags are set by the client to indicate how the DHCP server should its reply -- either unicast or broadcast.', + ignore_above: 1024, + description: 'Region ISO code.', + example: 'CA-QC', }, { - name: 'client_ip', - type: 'ip', - description: 'The current IP address of the client.', + name: 'geo.region_name', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Region name.', + example: 'Quebec', }, { - name: 'assigned_ip', - type: 'ip', + name: 'hostname', + level: 'core', + type: 'keyword', + ignore_above: 1024, description: - 'The IP address that the DHCP server is assigning to the client. This field is also known as "your" IP address.', + 'Hostname of the host.\n\nIt normally contains what the `hostname` command returns on the host machine.', }, { - name: 'server_ip', - type: 'ip', + name: 'id', + level: 'core', + type: 'keyword', + ignore_above: 1024, description: - 'The IP address of the DHCP server that the client should use for the next step in the bootstrap process.', + '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: 'relay_ip', + name: 'ip', + level: 'core', type: 'ip', - description: - 'The relay IP address used by the client to contact the server (i.e. a DHCP relay server).', + description: 'Host ip addresses.', }, { - name: 'client_mac', + name: 'mac', + level: 'core', type: 'keyword', - description: "The client's MAC address (layer two).", + ignore_above: 1024, + description: 'Host mac addresses.', }, { - name: 'server_name', + name: 'name', + level: 'core', type: 'keyword', + ignore_above: 1024, description: - 'The name of the server sending the message. Optional. Used in DHCPOFFER or DHCPACK messages.', + '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: 'op_code', + name: 'os.family', + level: 'extended', type: 'keyword', - example: 'bootreply', - description: 'The message op code (bootrequest or bootreply).', - }, - { - name: 'hops', - type: 'long', - description: 'The number of hops the DHCP message went through.', + ignore_above: 1024, + description: 'OS family (such as redhat, debian, freebsd, windows).', + example: 'debian', }, { - name: 'hardware_type', + name: 'os.full', + level: 'extended', type: 'keyword', - description: - 'The type of hardware used for the local network (Ethernet, LocalTalk, etc).', - }, - { - name: 'option', - type: 'group', - fields: [ - { - name: 'message_type', - type: 'keyword', - example: 'ack', - description: - 'The specific type of DHCP message being sent (e.g. discover, offer, request, decline, ack, nak, release, inform).', - }, - { - name: 'parameter_request_list', - type: 'keyword', - description: - 'This option is used by a DHCP client to request values for specified configuration parameters.', - }, - { - name: 'requested_ip_address', - type: 'ip', - description: - 'This option is used in a client request (DHCPDISCOVER) to allow the client to request that a particular IP address be assigned.', - }, - { - name: 'server_identifier', - type: 'ip', - description: 'IP address of the individual DHCP server which handled this message.', - }, - { - name: 'broadcast_address', - type: 'ip', - description: - "This option specifies the broadcast address in use on the client's subnet. ", - }, - { - name: 'max_dhcp_message_size', - type: 'long', - description: - 'This option specifies the maximum length DHCP message that the client is willing to accept.', - }, - { - name: 'class_identifier', - type: 'keyword', - 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: 'domain_name', - type: 'keyword', - description: - 'This option specifies the domain name that client should use when resolving hostnames via the Domain Name System.', - }, - { - name: 'dns_servers', - type: 'ip', - description: - 'The domain name server option specifies a list of Domain Name System servers available to the client.', - }, - { - name: 'vendor_identifying_options', - type: 'object', - 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: 'subnet_mask', - type: 'ip', - description: 'The subnet mask that the client should use on the currnet network.', - }, - { - name: 'utc_time_offset_sec', - type: 'long', - description: - "The time offset field specifies the offset of the client's subnet in seconds from Coordinated Universal Time (UTC). ", - }, - { - name: 'router', - type: 'ip', - description: - "The router option specifies a list of IP addresses for routers on the client's subnet. ", - }, - { - name: 'time_servers', - type: 'ip', - description: - 'The time server option specifies a list of RFC 868 time servers available to the client.', - }, - { - name: 'ntp_servers', - type: 'ip', - description: - 'This option specifies a list of IP addresses indicating NTP servers available to the client.', - }, + ignore_above: 1024, + multi_fields: [ { - name: 'hostname', - type: 'keyword', - description: 'This option specifies the name of the client.', - }, - { - name: 'ip_address_lease_time_sec', - type: 'long', - 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: 'message', + name: 'text', type: 'text', - 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: 'renewal_time_sec', - type: 'long', - description: - 'This option specifies the time interval from address assignment until the client transitions to the RENEWING state.', - }, - { - name: 'rebinding_time_sec', - type: 'long', - description: - 'This option specifies the time interval from address assignment until the client transitions to the REBINDING state.', - }, - { - name: 'boot_file_name', - type: 'keyword', - description: - "This option is used to identify a bootfile when the 'file' field in the DHCP header has been used for DHCP options. ", + norms: false, + default_field: false, }, ], + description: 'Operating system name, including the version or code name.', + example: 'Mac OS Mojave', }, - ], - }, - ], - }, - { - key: 'dns', - title: 'DNS', - description: 'DNS-specific event fields.', - fields: [ - { - name: 'dns', - type: 'group', - fields: [ { - name: 'id', - type: 'long', - description: - 'The DNS packet identifier assigned by the program that generated the query. The identifier is copied to the response.', + 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: 'op_code', - 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: '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: 'flags.authoritative', - type: 'boolean', - description: - 'A DNS flag specifying that the responding server is an authority for the domain name used in the question.', + name: 'os.platform', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Operating system platform (such centos, ubuntu, windows).', + example: 'darwin', }, { - name: 'flags.recursion_available', - type: 'boolean', - description: - 'A DNS flag specifying whether recursive query support is available in the name server.', + name: 'os.version', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Operating system version as a raw string.', + example: '10.14.1', }, { - name: 'flags.recursion_desired', - type: 'boolean', + name: 'type', + level: 'core', + type: 'keyword', + ignore_above: 1024, description: - 'A DNS flag specifying that the client directs the server to pursue a query recursively. Recursive query support is optional.', + '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: 'flags.authentic_data', - type: 'boolean', - description: - 'A DNS flag specifying that the recursive server considers the response authentic.', + name: 'uptime', + level: 'extended', + type: 'long', + description: 'Seconds the host has been up.', + example: 1325, }, { - name: 'flags.checking_disabled', - type: 'boolean', + name: 'user.domain', + level: 'extended', + type: 'keyword', + ignore_above: 1024, description: - 'A DNS flag specifying that the client disables the server signature validation of the query.', + 'Name of the directory the user is a member of.\n\nFor example, an LDAP or Active Directory domain name.', }, { - name: 'flags.truncated_response', - type: 'boolean', - description: - 'A DNS flag specifying that only the first 512 bytes of the reply were returned.', + name: 'user.email', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'User email address.', }, { - name: 'response_code', - description: 'The DNS status code.', - example: 'NOERROR', + 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: 'question.name', + name: 'user.group.domain', + level: 'extended', + type: 'keyword', + ignore_above: 1024, description: - 'The domain name being queried. If the name field contains non-printable characters (below 32 or above 126), then those characters are represented as escaped base 10 integers (\\DDD). Back slashes and quotes are escaped. Tabs, carriage returns, and line feeds are converted to \\t, \\r, and respectively.', - example: 'www.google.com.', - }, - { - name: 'question.type', - description: 'The type of records being queried.', - example: 'AAAA', + 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', }, { - name: 'question.class', - description: 'The class of of records being queried.', - example: 'IN', + name: 'user.group.id', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Unique identifier for the group on the system/platform.', }, { - name: 'question.etld_plus_one', - 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: 'user.group.name', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Name of the group.', }, { - name: 'answers', - type: 'object', + name: 'user.hash', + level: 'extended', + type: 'keyword', + ignore_above: 1024, description: - 'An array containing a dictionary about each answer section returned by the server.', + '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: 'answers_count', - type: 'long', - description: 'The number of resource records contained in the `dns.answers` field.', + name: 'user.id', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Unique identifiers of the user.', }, { - name: 'answers.name', - description: 'The domain name to which this resource record pertains.', - example: 'example.com.', + 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: 'answers.type', - description: 'The type of data contained in this resource record.', - example: 'MX', + name: 'request.body.bytes', + level: 'extended', + type: 'long', + format: 'bytes', + description: 'Size in bytes of the request body.', + example: 887, }, { - name: 'answers.class', - description: 'The class of DNS data contained in this resource record.', - example: 'IN', + 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: 'answers.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.', + name: 'request.bytes', + level: 'extended', type: 'long', + format: 'bytes', + description: 'Total size in bytes of the request (body and headers).', + example: 1437, }, { - name: 'answers.data', + name: 'request.method', + level: 'extended', + type: 'keyword', + ignore_above: 1024, description: - 'The data describing the resource. The meaning of this data depends on the type and class of the resource record.', + '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: 'authorities', - type: 'object', - description: - 'An array containing a dictionary for each authority section from the answer.', + name: 'request.referrer', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Referrer for this HTTP request.', + example: 'https://blog.example.com/', }, { - name: 'authorities_count', + name: 'response.body.bytes', + level: 'extended', 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.', - }, - { - name: 'authorities.name', - description: 'The domain name to which this resource record pertains.', - example: 'example.com.', + format: 'bytes', + description: 'Size in bytes of the response body.', + example: 887, }, { - name: 'authorities.type', - description: 'The type of data contained in this resource record.', - example: 'NS', + 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: 'authorities.class', - description: 'The class of DNS data contained in this resource record.', - example: 'IN', + name: 'response.bytes', + level: 'extended', + type: 'long', + format: 'bytes', + description: 'Total size in bytes of the response (body and headers).', + example: 1437, }, { - name: 'additionals', - type: 'object', - description: - 'An array containing a dictionary for each additional section from the answer.', - }, - { - name: 'additionals_count', + name: 'response.status_code', + level: 'extended', 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.', - }, - { - 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', + format: 'string', + description: 'HTTP response status code.', + example: 404, }, { - 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.', - type: 'long', + 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: 'additionals.data', + name: 'alias', + level: 'extended', + type: 'keyword', + ignore_above: 1024, description: - 'The data describing the resource. The meaning of this data depends on the type and class of the resource record.', + '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: '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: '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: 'opt.udp_size', - type: 'long', - description: "Requestor's UDP payload size (in bytes).", + name: 'name', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Interface name as reported by the system.', + example: 'eth0', + default_field: false, }, ], }, - ], - }, - { - key: 'http', - title: 'HTTP', - description: 'HTTP-specific event fields.', - fields: [ { - name: 'http', + 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', - 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.', - }, - { - name: 'params', - type: 'alias', - migration: true, - path: 'url.query', - }, - ], + 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: '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.', - }, - { - name: 'code', - type: 'alias', - migration: true, - path: 'http.response.status_code', - }, - { - name: 'phrase', - type: 'alias', - migration: true, - path: 'http.response.status_phrase', - }, - ], + 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', }, - ], - }, - ], - }, - { - key: 'icmp', - title: 'ICMP', - description: 'ICMP specific event fields.', - fields: [ - { - name: 'icmp', - type: 'group', - fields: [ { - name: 'version', - description: 'The version of the ICMP protocol.', - possible_values: [4, 6], + 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: 'request.message', + name: 'origin.function', + level: 'extended', type: 'keyword', - description: 'A human readable form of the request.', + ignore_above: 1024, + description: 'The name of the function or method which originated the log event.', + example: 'init', }, { - name: 'request.type', - type: 'long', - description: 'The request type.', + 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: 'request.code', + name: 'syslog.facility.code', + level: 'extended', type: 'long', - description: 'The request code.', + 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: 'response.message', + name: 'syslog.facility.name', + level: 'extended', type: 'keyword', - description: 'A human readable form of the response.', + ignore_above: 1024, + description: 'The Syslog text-based facility of the log event, if available.', + example: 'local7', }, { - name: 'response.type', + name: 'syslog.priority', + level: 'extended', type: 'long', - description: 'The response type.', + 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: 'response.code', + name: 'syslog.severity.code', + level: 'extended', type: 'long', - description: 'The response code.', + 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', }, ], }, - ], - }, - { - key: 'memcache', - title: 'Memcache', - description: 'Memcached-specific event fields', - fields: [ { - name: 'memcache', + 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: 'protocol_type', + name: 'application', + level: 'extended', type: 'keyword', + ignore_above: 1024, 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.', + '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: 'request.line', - type: 'keyword', - description: 'The raw command line for unknown commands ONLY.', + 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: 'request.command', + name: 'community_id', + level: 'extended', type: 'keyword', + ignore_above: 1024, 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.', + '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: 'response.command', + name: 'direction', + level: 'core', type: 'keyword', + ignore_above: 1024, description: - 'Either the text based protocol response message type or the name of the originating request if binary protocol is used.', + "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: '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".', + 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: 'response.type', + name: 'iana_number', + level: 'extended', type: 'keyword', + ignore_above: 1024, 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).', + '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: 'response.error_msg', - type: 'keyword', + name: 'inner', + level: 'extended', + type: 'object', + object_type: 'keyword', description: - 'The optional error message in the memcache response (text based protocol only).', + '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: 'request.opcode', + name: 'inner.vlan.id', + level: 'extended', type: 'keyword', - description: 'The binary protocol message opcode name.', + ignore_above: 1024, + description: 'VLAN ID as reported by the observer.', + example: 10, + default_field: false, }, { - name: 'response.opcode', + name: 'inner.vlan.name', + level: 'extended', type: 'keyword', - description: 'The binary protocol message opcode name.', + ignore_above: 1024, + description: 'Optional VLAN name as reported by the observer.', + example: 'outside', + default_field: false, }, { - name: 'request.opcode_value', - type: 'long', - description: 'The binary protocol message opcode value.', - }, - { - name: 'response.opcode_value', - type: 'long', - description: 'The binary protocol message opcode value.', + name: 'name', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Name given by operators to sections of their network.', + example: 'Guest Wifi', }, { - name: 'request.opaque', + name: 'packets', + level: 'core', type: 'long', description: - 'The binary protocol opaque header value used for correlating request with response messages.', + 'Total packets transferred in both directions.\n\nIf `source.packets` and `destination.packets` are known, `network.packets`\nis their sum.', + example: 24, }, { - name: 'response.opaque', - type: 'long', + name: 'protocol', + level: 'core', + type: 'keyword', + ignore_above: 1024, description: - 'The binary protocol opaque header value used for correlating request with response messages.', - }, - { - name: 'request.vbucket', - type: 'long', - description: 'The vbucket index sent in the binary message.', + '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: 'response.status', + name: 'transport', + level: 'core', type: 'keyword', + ignore_above: 1024, description: - 'The textual representation of the response error code (binary protocol only).', + '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: 'response.status_code', - type: 'long', - description: 'The status code value returned in the response (binary protocol only).', + 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: 'request.keys', - type: 'array', - description: 'The list of keys sent in the store or load commands.', + name: 'vlan.id', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'VLAN ID as reported by the observer.', + example: 10, + default_field: false, }, { - name: 'response.keys', - type: 'array', - description: 'The list of keys returned for the load command (if present).', + 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: 'request.count_values', - type: 'long', + name: 'egress', + level: 'extended', + type: 'object', + object_type: 'keyword', description: - 'The number of values found in the memcache request message. If the command does not send any data, this field is missing.', + '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: 'response.count_values', - type: 'long', + name: 'egress.interface.alias', + level: 'extended', + type: 'keyword', + ignore_above: 1024, description: - 'The number of values found in the memcache response message. If the command does not send any data, this field is missing.', + '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: 'request.values', - type: 'array', - description: 'The list of base64 encoded values sent with the request (if present).', + 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: 'response.values', - type: 'array', - description: 'The list of base64 encoded values sent with the response (if present).', + 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: 'request.bytes', - type: 'long', - format: 'bytes', - description: 'The byte count of the values being transferred.', + 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: 'response.bytes', - type: 'long', - format: 'bytes', - description: 'The byte count of the values being transferred.', + 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: 'request.delta', - type: 'long', - description: 'The counter increment/decrement delta value.', + 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: 'request.initial', - type: 'long', - description: - 'The counter increment/decrement initial value parameter (binary protocol only).', + name: 'geo.city_name', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'City name.', + example: 'Montreal', }, { - name: 'request.verbosity', - type: 'long', - description: 'The value of the memcache "verbosity" command.', + name: 'geo.continent_name', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Name of the continent.', + example: 'North America', }, { - name: 'request.raw_args', + name: 'geo.country_iso_code', + level: 'core', type: 'keyword', - description: - 'The text protocol raw arguments for the "stats ..." and "lru crawl ..." commands.', + ignore_above: 1024, + description: 'Country ISO code.', + example: 'CA', }, { - name: 'request.source_class', - type: 'long', - description: "The source class id in 'slab reassign' command. ", + name: 'geo.country_name', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Country name.', + example: 'Canada', }, { - name: 'request.dest_class', - type: 'long', - description: "The destination class id in 'slab reassign' command. ", + name: 'geo.location', + level: 'core', + type: 'geo_point', + description: 'Longitude and latitude.', + example: '{ "lon": -73.614830, "lat": 45.505918 }', }, { - name: 'request.automove', + name: 'geo.name', + level: 'extended', type: 'keyword', + ignore_above: 1024, 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.', + '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: 'request.flags', - type: 'long', - description: 'The memcache command flags sent in the request (if present).', + name: 'geo.region_iso_code', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Region ISO code.', + example: 'CA-QC', }, { - name: 'response.flags', - type: 'long', - description: 'The memcache message flags sent in the response (if present).', + name: 'geo.region_name', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Region name.', + example: 'Quebec', }, { - name: 'request.exptime', - type: 'long', + name: 'hostname', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Hostname of the observer.', + }, + { + name: 'ingress', + level: 'extended', + type: 'object', + object_type: 'keyword', 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).', + '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: 'request.sleep_us', - type: 'long', - description: "The sleep setting in microseconds for the 'lru_crawler sleep' command. ", + 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: 'response.value', - type: 'long', - description: 'The counter value returned by a counter operation.', + 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: 'request.noreply', - type: 'boolean', - description: - 'Set to true if noreply was set in the request. The `memcache.response` field will be missing.', + 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: 'request.quiet', - type: 'boolean', + 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: - 'Set to true if the binary protocol message is to be treated as a quiet message.', + '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: 'request.cas_unique', - type: 'long', - description: 'The CAS (compare-and-swap) identifier if present.', + name: 'ip', + level: 'core', + type: 'ip', + description: 'IP addresses of the observer.', }, { - name: 'response.cas_unique', - type: 'long', + 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: - 'The CAS (compare-and-swap) identifier to be used with CAS-based updates (if present).', + '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: 'response.stats', - type: 'array', + name: 'type', + level: 'core', + type: 'keyword', + ignore_above: 1024, description: - 'The list of statistic values returned. Each entry is a dictionary with the fields "name" and "value".', + '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: 'response.version', + name: 'version', + level: 'core', type: 'keyword', - description: 'The returned memcache version string.', + ignore_above: 1024, + description: 'Observer version.', }, ], }, - ], - }, - { - 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.', - fields: [ { - name: 'mongodb', + 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: 'error', - description: - 'If the MongoDB request has resulted in an error, this field contains the error message returned by the server.', + name: 'id', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Unique identifier for the organization.', }, { - 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.', + 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: 'numberToSkip', - type: 'long', + 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: - 'Sets the number of documents to omit - starting from the first document in the resulting dataset - when returning the result of the query.', + '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: 'numberToReturn', - type: 'long', - description: 'The requested maximum number of documents to be returned.', + name: 'checksum', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Checksum of the installed package for verification.', + example: '68b329da9893e34099c7d8ad5cb9c940', }, { - name: 'numberReturned', - type: 'long', - description: 'The number of documents in the reply.', + 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: 'startingFrom', - description: 'Where in the cursor this reply is starting.', + 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: 'query', + name: 'installed', + level: 'extended', + type: 'date', + description: 'Time when package was installed.', + }, + { + name: 'license', + level: 'extended', + type: 'keyword', + ignore_above: 1024, 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.', + '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: 'returnFieldsSelector', + name: 'reference', + level: 'extended', + type: 'keyword', + ignore_above: 1024, 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.', + '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: 'selector', + name: 'type', + level: 'extended', + type: 'keyword', + ignore_above: 1024, description: - 'A BSON document that specifies the query for selecting the document to update or delete.', + '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: 'update', + name: 'messages', + type: 'alias', + migration: true, + path: 'event.original', 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.', + '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: 'cursorId', + name: 'warnings', + type: 'alias', + migration: true, + path: 'error.message', description: - 'The cursor identifier returned in the OP_REPLY. This must be the value that was returned from the database.', + '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', }, ], }, - ], - }, - { - key: 'mysql', - title: 'MySQL', - description: 'MySQL-specific event fields.', - fields: [ { - name: 'mysql', + 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: 'affected_rows', - type: 'long', - description: - 'If the MySQL command is successful, this field contains the affected number of rows of the last statement.', - }, - { - name: 'insert_id', - description: - 'If the INSERT query is successful, this field contains the id of the newly inserted row.', - }, - { - name: 'num_fields', - description: - 'If the SELECT query is successful, this field is set to the number of fields returned.', + name: 'continent_name', + type: 'keyword', + description: 'The name of the continent.\n', }, { - name: 'num_rows', - description: - 'If the SELECT query is successful, this field is set to the number of rows returned.', + name: 'city_name', + type: 'keyword', + description: 'The name of the city.\n', }, { - name: 'query', - description: "The row mysql query as read from the transaction's request. ", + name: 'region_name', + type: 'keyword', + description: 'The name of the region.\n', }, { - name: 'error_code', - type: 'long', - description: 'The error code returned by MySQL.', + name: 'country_iso_code', + type: 'keyword', + description: 'Country ISO code.\n', }, { - name: 'error_message', - description: 'The error info message returned by MySQL.', + name: 'location', + type: 'geo_point', + description: 'The longitude and latitude.\n', }, ], }, ], }, { - key: 'nfs', - title: 'NFS', - description: 'NFS v4/3 specific event fields.', + key: 'file_integrity', + title: 'File Integrity', + description: 'These are the fields generated by the file_integrity module.', 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.', - }, - { - name: 'status', - description: 'NFS operation reply status.', - }, - ], - }, - { - name: 'rpc', + name: 'hash', type: 'group', - description: 'ONC RPC specific event fields.', + description: + 'Hashes of the file. The keys are algorithm names and the values are the hex encoded digest values.\n', 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: 'blake2b_256', + type: 'keyword', + description: 'BLAKE2b-256 hash of the file.', }, { - name: 'cred.stamp', - type: 'long', - description: 'Arbitrary ID which the caller machine may generate.', + name: 'blake2b_384', + type: 'keyword', + description: 'BLAKE2b-384 hash of the file.', }, { - name: 'cred.machinename', - description: "The name of the caller's machine.", + name: 'blake2b_512', + type: 'keyword', + description: 'BLAKE2b-512 hash of the file.', }, { - name: 'call_size', - type: 'alias', - path: 'source.bytes', - migration: true, - description: 'RPC call size with argument.', + name: 'md5', + overwrite: true, + type: 'keyword', + description: 'MD5 hash of the file.', }, { - name: 'reply_size', - type: 'alias', - path: 'destination.bytes', - migration: true, - description: 'RPC reply size with argument.', + name: 'sha1', + overwrite: true, + type: 'keyword', + description: 'SHA1 hash of the file.', }, - ], - }, - ], - }, - { - key: 'pgsql', - title: 'PostgreSQL', - description: 'PostgreSQL-specific event fields.', - fields: [ - { - name: 'pgsql', - type: 'group', - fields: [ { - name: 'error_code', - description: 'The PostgreSQL error code.', - type: 'long', + name: 'sha224', + type: 'keyword', + description: 'SHA224 hash of the file.', }, { - name: 'error_message', - description: 'The PostgreSQL error message.', + name: 'sha256', + overwrite: true, + type: 'keyword', + description: 'SHA256 hash of the file.', }, { - name: 'error_severity', - description: 'The PostgreSQL error severity.', - possible_values: ['ERROR', 'FATAL', 'PANIC'], + name: 'sha384', + type: 'keyword', + description: 'SHA384 hash of the file.', }, { - name: 'num_fields', - description: - 'If the SELECT query if successful, this field is set to the number of fields returned.', + name: 'sha3_224', + type: 'keyword', + description: 'SHA3_224 hash of the file.', }, { - name: 'num_rows', - description: - 'If the SELECT query if successful, this field is set to the number of rows returned.', + name: 'sha3_256', + type: 'keyword', + description: 'SHA3_256 hash of the file.', }, - ], - }, - ], - }, - { - key: 'redis', - title: 'Redis', - description: 'Redis-specific event fields.', - fields: [ - { - name: 'redis', - type: 'group', - fields: [ { - name: 'return_value', - description: 'The return value of the Redis command in a human readable format.', + name: 'sha3_384', + type: 'keyword', + description: 'SHA3_384 hash of the file.', }, { - name: 'error', - description: - 'If the Redis command has resulted in an error, this field contains the error message returned by the Redis server.', + name: 'sha3_512', + type: 'keyword', + description: 'SHA3_512 hash of the file.', }, - ], - }, - ], - }, - { - key: 'thrift', - title: 'Thrift-RPC', - description: 'Thrift-RPC specific event fields.', - 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.', + name: 'sha512', + overwrite: true, + type: 'keyword', + description: 'SHA512 hash of the file.', }, { - name: 'service', - description: 'The name of the Thrift-RPC service as defined in the IDL files.', + name: 'sha512_224', + type: 'keyword', + description: 'SHA512/224 hash of the file.', }, { - name: 'return_value', - description: - 'The value returned by the Thrift-RPC call. This is encoded in a human readable format.', + name: 'sha512_256', + type: 'keyword', + description: 'SHA512/256 hash of the file.', }, { - name: 'exceptions', - description: - 'If the call resulted in exceptions, this field contains the exceptions in a human readable format.', + name: 'xxh64', + type: 'keyword', + description: 'XX64 hash of the file.', }, ], }, ], }, { - key: 'tls', - title: 'TLS', - description: 'TLS-specific event fields.', + key: 'system', + title: 'System', + description: 'These are the fields generated by the system module.\n', + release: 'beta', fields: [ { - name: 'tls', + name: 'event', type: 'group', fields: [ { - name: 'version', + name: 'origin', type: 'keyword', - description: 'The version of the TLS protocol used.', - example: 'TLS 1.3', - }, - { - name: 'handshake_completed', - type: 'boolean', description: - 'Whether the TLS negotiation has been successful and the session has transitioned to encrypted mode.', - }, - { - name: 'resumed', - type: 'boolean', - description: 'If the TLS session has been resumed from a previous session.', + '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: 'resumption_method', + name: 'entity_id', 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.', + '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: 'client_certificate_requested', - type: 'boolean', - description: - 'Whether the server has requested the client to authenticate itself using a client certificate.', + name: 'terminal', + type: 'keyword', + description: 'Terminal of the user.\n', }, + ], + }, + { + name: 'process', + type: 'group', + fields: [ { - name: 'client_hello', + 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: 'version', + name: 'blake2b_256', type: 'keyword', - description: - 'The version of the TLS protocol by which the client wishes to communicate during this session.', + description: 'BLAKE2b-256 hash of the executable.', }, { - name: 'supported_ciphers', - type: 'array', - description: - 'List of ciphers the client is willing to use for this session. See https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4', + name: 'blake2b_384', + type: 'keyword', + description: 'BLAKE2b-384 hash of the executable.', }, { - name: 'supported_compression_methods', - type: 'array', - description: - 'The list of compression methods the client supports. See https://www.iana.org/assignments/comp-meth-ids/comp-meth-ids.xhtml', + name: 'blake2b_512', + type: 'keyword', + description: 'BLAKE2b-512 hash of the executable.', }, { - 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.', - }, - { - name: 'session_ticket', - type: 'keyword', - description: - 'Length of the session ticket, if provided, or an empty string to advertise support for tickets.', - }, - { - name: 'supported_versions', - type: 'keyword', - description: 'List of TLS versions that the client is willing to use.', - }, - { - name: 'supported_groups', - type: 'keyword', - description: - 'List of Elliptic Curve Cryptography (ECC) curve groups supported by the client.', - }, - { - name: 'signature_algorithms', - type: 'keyword', - description: - 'List of signature algorithms that may be use in digital signatures.', - }, - { - 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.', - }, - { - name: '_unparsed_', - type: 'keyword', - description: 'List of extensions that were left unparsed by Packetbeat.', - }, - ], + name: 'sha224', + type: 'keyword', + description: 'SHA224 hash of the executable.', }, - ], - }, - { - name: 'server_hello', - type: 'group', - fields: [ { - name: 'version', + name: 'sha384', 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.', + description: 'SHA384 hash of the executable.', }, { - name: 'selected_cipher', + name: 'sha3_224', type: 'keyword', - description: - 'The cipher suite selected by the server from the list provided by in the client hello.', + description: 'SHA3_224 hash of the executable.', }, { - name: 'selected_compression_method', + name: 'sha3_256', type: 'keyword', - description: - 'The compression method selected by the server from the list provided in the client hello.', + description: 'SHA3_256 hash of the executable.', }, { - name: 'session_id', + name: 'sha3_384', type: 'keyword', - description: - 'Unique number to identify the session for the corresponding connection with the client.', + description: 'SHA3_384 hash of the executable.', }, { - name: 'extensions', - type: 'group', - description: 'The hello extensions provided by the server.', - fields: [ - { - name: 'application_layer_protocol_negotiation', - type: 'array', - 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.', - }, - { - name: 'supported_versions', - type: 'keyword', - description: 'Negotiated TLS version to be used.', - }, - { - 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.', - }, - { - name: '_unparsed_', - type: 'keyword', - description: 'List of extensions that were left unparsed by Packetbeat.', - }, - ], + 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: 'client_certificate', + name: 'host', type: 'group', - description: 'Certificate provided by the client for authentication.', + description: '`host` contains general host information.\n', + release: 'beta', fields: [ { - name: 'version', + name: 'uptime', type: 'long', - description: 'X509 format version.', - }, - { - name: 'serial_number', - type: 'keyword', - description: "The certificate's serial number.", + format: 'duration', + input_format: 'nanoseconds', + output_format: 'asDays', + output_precision: 1, + description: 'Uptime in nanoseconds.\n', }, { - name: 'not_before', + name: 'boottime', type: 'date', - description: 'Date before which the certificate is not valid.', + description: 'Boot time.\n', }, { - name: 'not_after', - type: 'date', - description: 'Date after which the certificate expires.', + name: 'containerized', + type: 'boolean', + description: 'Set if host is a container.\n', }, { - name: 'public_key_algorithm', + name: 'timezone.name', type: 'keyword', - description: - "The algorithm used for this certificate's public key. One of RSA, DSA or ECDSA. ", + description: 'Name of the timezone of the host, e.g. BST.\n', }, { - name: 'public_key_size', + name: 'timezone.offset.sec', type: 'long', - description: 'Size of the public key.', + description: 'Timezone offset in seconds.\n', + }, + { + name: 'hostname', + type: 'keyword', + description: 'Hostname.\n', }, { - name: 'signature_algorithm', + name: 'id', type: 'keyword', - description: "The algorithm used for the certificate's signature. ", + description: 'Host ID.\n', }, { - name: 'alternative_names', - type: 'array', - description: 'Subject Alternative Names for this certificate.', + name: 'architecture', + type: 'keyword', + description: 'Host architecture (e.g. x86_64).\n', }, { - name: 'raw', + name: 'mac', type: 'keyword', - description: 'The raw certificate in PEM format.', + description: 'MAC addresses.\n', }, { - 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: 'ip', + type: 'ip', + description: 'IP addresses.\n', }, { - name: 'issuer', + name: 'os', type: 'group', - description: 'Entity that issued and signed this certificate.', + description: '`os` contains information about the operating system.\n', fields: [ { - name: 'country', + name: 'codename', type: 'keyword', - description: 'Country code.', + description: 'OS codename, if any (e.g. stretch).\n', }, { - name: 'organization', + name: 'platform', type: 'keyword', - description: 'Organization name.', + description: 'OS platform (e.g. centos, ubuntu, windows).\n', }, { - name: 'organizational_unit', + name: 'name', type: 'keyword', - description: 'Unit within organization.', + description: 'OS name (e.g. Mac OS X).\n', }, { - 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: 'fingerprint', - type: 'group', - fields: [ - { - name: 'md5', + name: 'family', type: 'keyword', - description: "Certificate's MD5 fingerprint.", + description: 'OS family (e.g. redhat, debian, freebsd, windows).\n', }, { - name: 'sha1', + name: 'version', type: 'keyword', - description: "Certificate's SHA-1 fingerprint.", + description: 'OS version.\n', }, { - name: 'sha256', + name: 'kernel', type: 'keyword', - description: "Certificate's SHA-256 fingerprint.", + description: "The operating system's kernel version.\n", }, ], }, ], }, { - name: 'server_certificate', + name: 'package', type: 'group', - description: 'Certificate provided by the server for authentication.', + 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: 'long', - description: 'X509 format version.', + type: 'keyword', + description: 'Package version.\n', }, { - name: 'serial_number', + name: 'release', type: 'keyword', - description: "The certificate's serial number.", + description: 'Package release.\n', }, { - name: 'not_before', - type: 'date', - description: 'Date before which the certificate is not valid.', + name: 'arch', + type: 'keyword', + description: 'Package architecture.\n', }, { - name: 'not_after', + name: 'license', + type: 'keyword', + description: 'Package license.\n', + }, + { + name: 'installtime', type: 'date', - description: 'Date after which the certificate expires.', + description: 'Package install time.\n', + }, + { + name: 'size', + type: 'long', + description: 'Package size.\n', + }, + { + name: 'summary', + description: 'Package summary.\n', }, { - name: 'public_key_algorithm', + name: 'url', type: 'keyword', - description: - "The algorithm used for this certificate's public key. One of RSA, DSA or ECDSA. ", + description: 'Package URL.\n', }, + ], + }, + { + name: 'user', + type: 'group', + description: '`user` contains information about the users on a system.\n', + release: 'beta', + fields: [ { - name: 'public_key_size', - type: 'long', - description: 'Size of the public key.', + name: 'name', + type: 'keyword', + description: 'User name.\n', }, { - name: 'signature_algorithm', + name: 'uid', type: 'keyword', - description: "The algorithm used for the certificate's signature. ", + description: 'User ID.\n', }, { - name: 'alternative_names', - type: 'array', - description: 'Subject Alternative Names for this certificate.', + name: 'gid', + type: 'keyword', + description: 'Group ID.\n', }, { - name: 'raw', + name: 'dir', type: 'keyword', - description: 'The raw certificate in PEM format.', + description: "User's home directory.\n", }, { - 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: 'shell', + type: 'keyword', + description: 'Program to run at login.\n', }, { - 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: 'user_information', + type: 'keyword', + description: 'General user information. On Linux, this is the gecos field.\n', }, { - name: 'fingerprint', - type: 'group', + 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: 'md5', + name: 'name', type: 'keyword', - description: "Certificate's MD5 fingerprint.", + description: 'Group name.\n', }, { - name: 'sha1', - type: 'keyword', - description: "Certificate's SHA-1 fingerprint.", - }, - { - name: 'sha256', - type: 'keyword', - description: "Certificate's SHA-256 fingerprint.", + name: 'gid', + type: 'integer', + description: 'Group ID.\n', }, ], }, - ], - }, - { - 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.', - }, - { - name: 'fingerprints', - type: 'group', - description: 'Fingerprints for this TLS session.', - fields: [ { - name: 'ja3', + name: 'password', type: 'group', - description: 'JA3 TLS client fingerprint', + description: + "`password` contains information about a user's password (not the password itself).\n", fields: [ { - name: 'hash', + name: 'type', type: 'keyword', - description: 'The JA3 fingerprint hash for the client side.', + 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: 'str', - type: 'keyword', - description: 'The JA3 string used to calculate the hash.', + name: 'last_changed', + type: 'date', + description: "The day the user's password was last changed.\n", }, ], }, diff --git a/x-pack/legacy/plugins/siem/server/utils/beat_schema/8.0.0/ecs.ts b/x-pack/legacy/plugins/siem/server/utils/beat_schema/8.0.0/ecs.ts index 34deee7fa8895..a439d105d63df 100644 --- a/x-pack/legacy/plugins/siem/server/utils/beat_schema/8.0.0/ecs.ts +++ b/x-pack/legacy/plugins/siem/server/utils/beat_schema/8.0.0/ecs.ts @@ -13,1997 +13,5663 @@ * instances e.g. `@timestamp` to `timestamp` */ -import { EcsSchema } from '../type'; +import { Schema } from '../type'; -export const ecsSchema: EcsSchema = { - agent: { - description: - 'The agent fields contain the data about the software entity, if any, that collects, detects, or observes events on a host, or takes measurements on a host. Examples include Beats. Agents may also run on observers. ECS agent.* fields shall be populated with details of the agent running on the host or observer where the event happened or the measurement was taken.\n', - fields: { - 'agent.ephemeral_id': { - description: - 'Ephemeral identifier of this agent (if one exists).\nThis id normally changes across restarts, but `agent.id` does not.', - example: '8a4f500f', - footnote: '', - group: 2, - level: 'extended', - name: 'agent.ephemeral_id', - required: false, - type: 'keyword', - }, - 'agent.id': { - description: - 'Unique identifier of this agent (if one exists).\nExample: For Beats this would be beat.id.', - example: '8a4f500d', - footnote: '', - group: 2, - level: 'core', - name: 'agent.id', - required: false, - type: 'keyword', - }, - 'agent.name': { - description: - 'Name of the agent.\nThis 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.\nIf no name is given, the name is often left empty.', - example: 'foo', - footnote: '', - group: 2, - level: 'core', - name: 'agent.name', - required: false, - type: 'keyword', - }, - 'agent.type': { - description: - 'Type of the agent.\nThe 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', - footnote: '', - group: 2, - level: 'core', - name: 'agent.type', - required: false, - type: 'keyword', - }, - 'agent.version': { - description: 'Version of the agent.', - example: '6.0.0-rc2', - footnote: '', - group: 2, - level: 'core', - name: 'agent.version', - required: false, - type: 'keyword', - }, - }, - group: 2, - name: 'agent', - title: 'Agent', - type: 'group', - }, - base: { - description: - 'The base set contains all fields which are on the top level. These fields are common across all types of events.\n', - fields: { - '@timestamp': { - description: - 'Date/time when the event originated.\nFor log events this is the date/time when the event was generated, and not when it was read.\nRequired field for all events.', - example: '2016-05-23T08:05:34.853Z', - footnote: '', - group: 1, - level: 'core', +export const ecsSchema: Schema = [ + { + key: 'ecs', + title: 'ECS', + description: 'ECS Fields.', + fields: [ + { name: '@timestamp', + level: 'core', required: true, type: 'date', - }, - labels: { description: - 'Key/value pairs.\nCan be used to add meta information to events. Should not contain nested objects. All values are stored as keyword.\nExample: `docker` and `k8s` labels.', - example: "{'application': 'foo-bar', 'env': 'production'}", - footnote: '', - group: 1, - level: 'core', + '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', - required: false, + level: 'core', type: 'object', - }, - message: { + object_type: 'keyword', description: - 'For log events the message field contains the log message.\nIn other use cases the message field can be used to concatenate different values which are then freely searchable. If multiple messages exist, they can be combined into one message.', - example: 'Hello World', - footnote: '', - group: 1, - level: 'core', + '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', - required: false, + 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', }, - tags: { - description: 'List of keywords used to tag each event.', - example: '["production", "env2"]', - footnote: '', - group: 1, - level: 'core', + { name: 'tags', - required: false, - type: 'keyword', - }, - }, - group: 1, - name: 'base', - title: 'Base', - type: 'group', - }, - client: { - description: - 'A client is defined as the initiator of a network connection for events regarding sessions, connections, or bidirectional flow records. For TCP events, the client is the initiator of the TCP connection that sends the SYN packet(s). For other protocols, the client is generally the initiator or requestor in the network transaction. Some systems use the term "originator" to refer the client in TCP connections. The client fields describe details about the system acting as the client in the network event. Client fields are usually populated in conjuction with server fields. Client fields are generally not populated for packet-level events. \n', - fields: { - 'client.bytes': { - description: 'Bytes sent from the client to the server.', - example: '184', - footnote: '', - group: 2, level: 'core', - name: 'client.bytes', - required: false, - type: 'long', - }, - 'client.domain': { - description: 'Client domain.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'client.domain', - required: false, - type: 'keyword', - }, - 'client.ip': { - description: 'IP address of the client.\nCan be one or multiple IPv4 or IPv6 addresses.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'client.ip', - required: false, - type: 'ip', - }, - 'client.mac': { - description: 'MAC address of the client.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'client.mac', - required: false, type: 'keyword', + ignore_above: 1024, + description: 'List of keywords used to tag each event.', + example: '["production", "env2"]', }, - 'client.packets': { - description: 'Packets sent from the client to the server.', - example: '12', - footnote: '', - group: 2, - level: 'core', - name: 'client.packets', - required: false, - type: 'long', - }, - 'client.port': { - description: 'Port of the client.', - example: '', - footnote: '', + { + name: 'agent', + title: 'Agent', group: 2, - level: 'core', - name: 'client.port', - required: false, - type: 'long', - }, - }, - group: 2, - name: 'client', - title: 'Client', - type: 'group', - }, - cloud: { - description: 'Fields related to the cloud or infrastructure the events are coming from.\n', - fields: { - 'cloud.account.id': { description: - 'The cloud account or organization id used to identify different entities in a multi-tenant environment.\nExamples: AWS account id, Google Cloud ORG Id, or other unique identifier.', - example: '666777888999', - footnote: '', - group: 2, - level: 'extended', - name: 'cloud.account.id', - required: false, - type: 'keyword', - }, - 'cloud.availability_zone': { - description: 'Availability zone in which this host is running.', - example: 'us-east-1c', - footnote: '', - group: 2, - level: 'extended', - name: 'cloud.availability_zone', - required: false, - type: 'keyword', - }, - 'cloud.instance.id': { - description: 'Instance ID of the host machine.', - example: 'i-1234567890abcdef0', - footnote: '', - group: 2, - level: 'extended', - name: 'cloud.instance.id', - required: false, - type: 'keyword', - }, - 'cloud.instance.name': { - description: 'Instance name of the host machine.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'cloud.instance.name', - required: false, - type: 'keyword', - }, - 'cloud.machine.type': { - description: 'Machine type of the host machine.', - example: 't2.medium', - footnote: '', - group: 2, - level: 'extended', - name: 'cloud.machine.type', - required: false, - type: 'keyword', - }, - 'cloud.provider': { - description: 'Name of the cloud provider. Example values are ec2, gce, or digitalocean.', - example: 'ec2', - footnote: '', - group: 2, - level: 'extended', - name: 'cloud.provider', - required: false, - type: 'keyword', - }, - 'cloud.region': { - description: 'Region in which this host is running.', - example: 'us-east-1', - footnote: '', - group: 2, - level: 'extended', - name: 'cloud.region', - required: false, - type: 'keyword', - }, - }, - group: 2, - name: 'cloud', - title: 'Cloud', - type: 'group', - }, - container: { - description: - 'Container fields are used for meta information about the specific container that is the source of information. These fields help correlate data based containers from any runtime.\n', - fields: { - 'container.id': { - description: 'Unique container id.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'container.id', - required: false, - type: 'keyword', - }, - 'container.image.name': { - description: 'Name of the image the container was built on.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'container.image.name', - required: false, - type: 'keyword', - }, - 'container.image.tag': { - description: 'Container image tag.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'container.image.tag', - required: false, - type: 'keyword', - }, - 'container.labels': { - description: 'Image labels.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'container.labels', - required: false, - type: 'object', - }, - 'container.name': { - description: 'Container name.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'container.name', - required: false, - type: 'keyword', - }, - 'container.runtime': { - description: 'Runtime managing this container.', - example: 'docker', - footnote: '', + '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, - level: 'extended', - name: 'container.runtime', - required: false, - type: 'keyword', - }, - }, - group: 2, - name: 'container', - title: 'Container', - type: 'group', - }, - destination: { - description: - 'Destination fields describe details about the destination of a packet/event. Destination fields are usually populated in conjunction with source fields.\n', - fields: { - 'destination.bytes': { - description: 'Bytes sent from the destination to the source.', - example: '184', - footnote: '', - group: 2, - level: 'core', - name: 'destination.bytes', - required: false, - type: 'long', - }, - 'destination.domain': { - description: 'Destination domain.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'destination.domain', - required: false, - type: 'keyword', - }, - 'destination.ip': { description: - 'IP address of the destination.\nCan be one or multiple IPv4 or IPv6 addresses.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'destination.ip', - required: false, - type: 'ip', - }, - 'destination.mac': { - description: 'MAC address of the destination.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'destination.mac', - required: false, - type: 'keyword', - }, - 'destination.packets': { - description: 'Packets sent from the destination to the source.', - example: '12', - footnote: '', - group: 2, - level: 'core', - name: 'destination.packets', - required: false, - type: 'long', - }, - 'destination.port': { - description: 'Port of the destination.', - example: '', - footnote: '', + '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, - level: 'core', - name: 'destination.port', - required: false, - type: 'long', - }, - }, - group: 2, - name: 'destination', - title: 'Destination', - type: 'group', - }, - ecs: { - description: 'Meta-information specific to ECS.\n', - fields: { - 'ecs.version': { description: - 'ECS version this event conforms to. `ecs.version` is a required field and must exist in all events.\nWhen querying across multiple indices -- which may conform to slightly different ECS versions -- this field lets integrations adjust to the schema version of the events.\nThe current version is 1.0.0-beta1 .', - example: '1.0.0-beta1', - footnote: '', - group: 2, - level: 'core', - name: 'ecs.version', - required: true, - type: 'keyword', - }, - }, - group: 2, - name: 'ecs', - title: 'ECS', - type: 'group', - }, - error: { - description: - 'These fields can represent errors of any kind. Use them for errors that happen while fetching events or in cases where the event itself contains an error.\n', - fields: { - 'error.code': { - description: 'Error code describing the error.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'error.code', - required: false, - type: 'keyword', - }, - 'error.id': { - description: 'Unique identifier for the error.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'error.id', - required: false, - type: 'keyword', - }, - 'error.message': { - description: 'Error message.', - example: '', - footnote: '', + '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, - level: 'core', - name: 'error.message', - required: false, - type: 'text', - }, - }, - group: 2, - name: 'error', - title: 'Error', - type: 'group', - }, - event: { - description: - 'The event fields are used for context information about the log or metric event itself. A log is defined as an event containing details of something that happened. Log events must include the time at which the thing happened. Examples of log events include a process starting on a host, a network packet being sent from a source to a destination, or a network connection between a client and a server being initiated or closed. A metric is defined as an event containing one or more numerical or categorical measurements and the time at which the measurement was taken. Examples of metric events include memory pressure measured on a host, or vulnerabilities measured on a scanned host.\n', - fields: { - 'event.action': { description: - 'The action captured by the event.\nThis 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', - footnote: '', + '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, - level: 'core', - name: 'event.action', - required: false, - type: 'keyword', - }, - 'event.category': { description: - 'Event category.\nThis contains high-level information about the contents of the event. It is more generic than `event.action`, in the sense that typically a category contains multiple actions. Warning: In future versions of ECS, we plan to provide a list of acceptable values for this field, please use with caution.', - example: 'user-management', - footnote: '', + '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, - level: 'core', - name: 'event.category', - required: false, - type: 'keyword', - }, - 'event.created': { description: - 'event.created contains the date when the event was created.\nThis timestamp is distinct from @timestamp in that @timestamp contains the processed timestamp. For logs these two timestamps can be different as the timestamp in the log line and when the event is read for example by Filebeat are not identical. `@timestamp` must contain the timestamp extracted from the log line, event.created when the log line is read. The same could apply to package capturing where @timestamp contains the timestamp extracted from the network package and event.created when the event was created.\nIn case the two timestamps are identical, @timestamp should be used.', - example: '', - footnote: '', + '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, - level: 'core', - name: 'event.created', - required: false, - type: 'date', - }, - 'event.dataset': { description: - 'Name of the dataset.\nThe concept of a `dataset` (fileset / metricset) is used in Beats as a subset of modules. It contains the information which is currently stored in metricset.name and metricset.module or fileset.name.', - example: 'stats', - footnote: '', + '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, - level: 'core', - name: 'event.dataset', - required: false, - type: 'keyword', - }, - 'event.duration': { description: - 'Duration of the event in nanoseconds.\nIf event.start and event.end are known this value should be the difference between the end and start time.', - example: '', - footnote: '', + '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, - level: 'core', - name: 'event.duration', - required: false, - type: 'long', - }, - 'event.end': { description: - 'event.end contains the date when the event ended or when the activity was last observed.', - example: '', - footnote: '', + '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, - level: 'extended', - name: 'event.end', - required: false, - type: 'date', - }, - 'event.hash': { description: - 'Hash (perhaps logstash fingerprint) of raw field to be able to demonstrate log integrity.', - example: '123456789012345678901234567890ABCD', - footnote: '', - group: 2, - level: 'extended', - name: 'event.hash', - required: false, - type: 'keyword', - }, - 'event.id': { - description: 'Unique ID to describe the event.', - example: '8a4f500d', - footnote: '', + '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, - level: 'core', - name: 'event.id', - required: false, - type: 'keyword', - }, - 'event.kind': { description: - 'The kind of the event.\nThis gives information about what type of information the event contains, without being specific to the contents of the event. Examples are `event`, `state`, `alarm`. Warning: In future versions of ECS, we plan to provide a list of acceptable values for this field, please use with caution.', - example: 'state', - footnote: '', + '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, - level: 'extended', - name: 'event.kind', - required: false, - type: 'keyword', - }, - 'event.module': { description: - 'Name of the module this data is coming from.\nThis information is coming from the modules used in Beats or Logstash.', - example: 'mysql', - footnote: '', + '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, - level: 'core', - name: 'event.module', - required: false, - type: 'keyword', - }, - 'event.original': { description: - 'Raw text message of entire event. Used to demonstrate log integrity.\nThis 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', - footnote: '', + '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, - level: 'core', - name: 'event.original', - required: false, - type: '(not indexed)', - }, - 'event.outcome': { description: - 'The outcome of the event.\nIf the event describes an action, this fields contains the outcome of that action. Examples outcomes are `success` and `failure`. Warning: In future versions of ECS, we plan to provide a list of acceptable values for this field, please use with caution.', - example: 'success', - footnote: '', + '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, - level: 'extended', - name: 'event.outcome', - required: false, - type: 'keyword', - }, - 'event.risk_score': { description: - "Risk score or priority of the event (e.g. security solutions). Use your system's original value here.", - example: '', - footnote: '', + '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, - level: 'core', - name: 'event.risk_score', - required: false, - type: 'float', - }, - 'event.risk_score_norm': { description: - 'Normalized risk score or priority of the event, on a scale of 0 to 100.\nThis 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.', - example: '', - footnote: '', + '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, - level: 'extended', - name: 'event.risk_score_norm', - required: false, - type: 'float', - }, - 'event.severity': { description: - "Severity describes the severity of the event. What the different severity values mean can very different between use cases. It's up to the implementer to make sure severities are consistent across events.", - example: '7', - footnote: '', + '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, - level: 'core', - name: 'event.severity', - required: false, - type: 'long', - }, - 'event.start': { description: - 'event.start contains the date when the event started or when the activity was first observed.', - example: '', - footnote: '', + '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, - level: 'extended', - name: 'event.start', - required: false, - type: 'date', - }, - 'event.timezone': { 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.\nAcceptable timezone formats are: a canonical ID (e.g. "Europe/Amsterdam"), abbreviated (e.g. "EST") or an HH:mm differential (e.g. "-05:00").', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'event.timezone', - required: false, - type: 'keyword', - }, - 'event.type': { - description: 'Reserved for future usage.\nPlease avoid using this field for user data.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'event.type', - required: false, - type: 'keyword', - }, - }, - group: 2, - name: 'event', - title: 'Event', - type: 'group', - }, - file: { - description: - 'A file is defined as a set of information that has been created on, or has existed on a filesystem. File objects can be associated with host events, network events, and/or file events (e.g., those produced by File Integrity Monitoring [FIM] products or services). File fields provide details about the affected file associated with the event or metric.\n', - fields: { - 'file.ctime': { - description: 'Last time file metadata changed.', - example: '', - footnote: '', + '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, - level: 'extended', - name: 'file.ctime', - required: false, - type: 'date', - }, - 'file.device': { - description: 'Device that is the source of the file.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'file.device', - required: false, - type: 'keyword', - }, - 'file.extension': { - description: 'File extension.\nThis should allow easy filtering by file extensions.', - example: 'png', - footnote: '', - group: 2, - level: 'extended', - name: 'file.extension', - required: false, - type: 'keyword', - }, - 'file.gid': { - description: 'Primary group ID (GID) of the file.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'file.gid', - required: false, - type: 'keyword', - }, - 'file.group': { - description: 'Primary group name of the file.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'file.group', - required: false, - type: 'keyword', - }, - 'file.inode': { - description: 'Inode representing the file in the filesystem.', - example: '', - footnote: '', + 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, - level: 'extended', - name: 'file.inode', - required: false, - type: 'keyword', - }, - 'file.mode': { - description: 'Mode of the file in octal representation.', - example: '416', - footnote: '', + 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, - level: 'extended', - name: 'file.mode', - required: false, - type: 'keyword', - }, - 'file.mtime': { - description: 'Last time file content was modified.', - example: '', - footnote: '', + 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, - level: 'extended', - name: 'file.mtime', - required: false, - type: 'date', - }, - 'file.owner': { - description: "File owner's username.", - example: '', - footnote: '', + 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, - level: 'extended', - name: 'file.owner', - required: false, - type: 'keyword', - }, - 'file.path': { - description: 'Path to the file.', - example: '', - footnote: '', + 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, - level: 'extended', - name: 'file.path', - required: false, - type: 'keyword', - }, - 'file.size': { - description: 'File size in bytes (field is only added when `type` is `file`).', - example: '', - footnote: '', + 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, - level: 'extended', - name: 'file.size', - required: false, - type: 'long', - }, - 'file.target_path': { - description: 'Target path for symlinks.', - example: '', - footnote: '', + 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, - level: 'extended', - name: 'file.target_path', - required: false, - type: 'keyword', - }, - 'file.type': { - description: 'File type (file, dir, or symlink).', - example: '', - footnote: '', + 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, - level: 'extended', - name: 'file.type', - required: false, - type: 'keyword', - }, - 'file.uid': { - description: 'The user ID (UID) or security identifier (SID) of the file owner.', - example: '', - footnote: '', + 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, - level: 'extended', - name: 'file.uid', - required: false, - type: 'keyword', - }, - }, - group: 2, - name: 'file', - title: 'File', - type: 'group', - }, - geo: { - description: - 'Geo fields can carry data about a specific location related to an event or geo information derived from an IP field.\n', - fields: { - 'geo.city_name': { - description: 'City name.', - example: 'Montreal', - footnote: '', + 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, - level: 'core', - name: 'geo.city_name', - required: false, - type: 'keyword', - }, - 'geo.continent_name': { - description: 'Name of the continent.', - example: 'North America', - footnote: '', + 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, - level: 'core', - name: 'geo.continent_name', - required: false, - type: 'keyword', - }, - 'geo.country_iso_code': { - description: 'Country ISO code.', - example: 'CA', - footnote: '', + 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, - level: 'core', - name: 'geo.country_iso_code', - required: false, - type: 'keyword', - }, - 'geo.country_name': { - description: 'Country name.', - example: 'Canada', - footnote: '', + 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, - level: 'core', - name: 'geo.country_name', - required: false, - type: 'keyword', - }, - 'geo.location': { - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - footnote: '', + 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, - level: 'core', - name: 'geo.location', - required: false, - type: 'geo_point', - }, - 'geo.name': { description: - 'User-defined description of a location, at the level of granularity they care about.\nCould be the name of their data centers, the floor number, if this describes a local physical entity, city names.\nNot typically used in automated geolocation.', - example: 'boston-dc', - footnote: '', + '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, - level: 'extended', - name: 'geo.name', - required: false, - type: 'keyword', - }, - 'geo.region_iso_code': { - description: 'Region ISO code.', - example: 'CA-QC', - footnote: '', + 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, - level: 'core', - name: 'geo.region_iso_code', - required: false, - type: 'keyword', - }, - 'geo.region_name': { - description: 'Region name.', - example: 'Quebec', - footnote: '', - group: 2, - level: 'core', - name: 'geo.region_name', - required: false, - type: 'keyword', - }, - }, - group: 2, - name: 'geo', - title: 'Geo', - type: 'group', - }, - group: { - description: 'The group fields are meant to represent groups that are relevant to the event.\n', - fields: { - 'group.id': { - description: 'Unique identifier for the group on the system/platform.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'group.id', - required: false, - type: 'keyword', - }, - 'group.name': { - description: 'Name of the group.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'group.name', - required: false, - type: 'keyword', - }, - }, - group: 2, - name: 'group', - title: 'Group', - type: 'group', - }, - host: { - description: - 'A host is defined as a general computing instance. ECS host.* fields should be populated with details about the host on which the event happened, or on which the measurement was taken. Host types include hardware, virtual machines, Docker containers, and Kubernetes nodes.\n', - fields: { - 'host.architecture': { - description: 'Operating system architecture.', - example: 'x86_64', - footnote: '', - group: 2, - level: 'core', - name: 'host.architecture', - required: false, - type: 'keyword', - }, - 'host.hostname': { - description: - 'Hostname of the host.\nIt normally contains what the `hostname` command returns on the host machine.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'host.hostname', - required: false, - type: 'keyword', - }, - 'host.id': { - description: - 'Unique host id.\nAs hostname is not always unique, use values that are meaningful in your environment.\nExample: The current usage of `beat.name`.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'host.id', - required: false, - type: 'keyword', - }, - 'host.ip': { - description: 'Host ip address.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'host.ip', - required: false, - type: 'ip', - }, - 'host.mac': { - description: 'Host mac address.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'host.mac', - required: false, - type: 'keyword', - }, - 'host.name': { - description: - 'Name of the host.\nIt 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.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'host.name', - required: false, - type: 'keyword', - }, - 'host.type': { - description: - 'Type of host.\nFor 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.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'host.type', - required: false, - type: 'keyword', - }, - }, - group: 2, - name: 'host', - title: 'Host', - type: 'group', - }, - http: { - description: 'Fields related to HTTP activity.\n', - fields: { - 'http.request.method': { - description: 'Http request method.', - example: 'GET, POST, PUT', - footnote: '', - group: 2, - level: 'extended', - name: 'http.request.method', - required: false, - type: 'keyword', - }, - 'http.request.referrer': { - description: 'Referrer for this HTTP request.', - example: 'https://blog.example.com/', - footnote: '', - group: 2, - level: 'extended', - name: 'http.request.referrer', - required: false, - type: 'keyword', - }, - 'http.response.body': { - description: 'The full http response body.', - example: 'Hello world', - footnote: '', - group: 2, - level: 'extended', - name: 'http.response.body', - required: false, - type: 'keyword', - }, - 'http.response.status_code': { - description: 'Http response status code.', - example: '404', - footnote: '', - group: 2, - level: 'extended', - name: 'http.response.status_code', - required: false, - type: 'long', - }, - 'http.version': { - description: 'Http version.', - example: '1.1', - footnote: '', - group: 2, - level: 'extended', - name: 'http.version', - required: false, - type: 'keyword', - }, - }, - group: 2, - name: 'http', - title: 'HTTP', - type: 'group', - }, - log: { - description: 'Fields which are specific to log events.\n', - fields: { - 'log.level': { - description: 'Log level of the log event.\nSome examples are `WARN`, `ERR`, `INFO`.', - example: 'ERR', - footnote: '', - group: 2, - level: 'core', - name: 'log.level', - required: false, - type: 'keyword', - }, - 'log.original': { - description: - "This is the original log message and contains the full log message before splitting it up in multiple parts.\nIn 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.\nThis 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', - footnote: '', - group: 2, - level: 'core', - name: 'log.original', - required: false, - type: '(not indexed)', - }, - }, - group: 2, - name: 'log', - title: 'Log', - type: 'group', - }, - network: { - description: - 'The network is defined as the communication path over which a host or network event happens. The network.* fields should be populated with details about the network activity associated with an event.\n', - fields: { - 'network.application': { - description: - 'A name given to an application. 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.', - example: 'AIM', - footnote: '', - group: 2, - level: 'extended', - name: 'network.application', - required: false, - type: 'keyword', - }, - 'network.bytes': { - description: - 'Total bytes transferred in both directions.\nIf `source.bytes` and `destination.bytes` are known, `network.bytes` is their sum.', - example: '368', - footnote: '', - group: 2, - level: 'core', - name: 'network.bytes', - required: false, - type: 'long', - }, - 'network.community_id': { - 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.\nLearn more at https://github.com/corelight/community-id-spec.', - example: '1:hO+sN4H+MG5MY/8hIrXPqc4ZQz0=', - footnote: '', - group: 2, - level: 'extended', - name: 'network.community_id', - required: false, - type: 'keyword', - }, - 'network.direction': { - 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', - footnote: '', - group: 2, - level: 'core', - name: 'network.direction', - required: false, - type: 'keyword', - }, - 'network.forwarded_ip': { - description: 'Host IP address when the source IP address is the proxy.', - example: '192.1.1.2', - footnote: '', - group: 2, - level: 'core', - name: 'network.forwarded_ip', - required: false, - type: 'ip', - }, - 'network.iana_number': { - 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', - footnote: '', - group: 2, - level: 'extended', - name: 'network.iana_number', - required: false, - type: 'keyword', - }, - 'network.name': { - description: 'Name given by operators to sections of their network.', - example: 'Guest Wifi', - footnote: '', - group: 2, - level: 'extended', - name: 'network.name', - required: false, - type: 'keyword', - }, - 'network.packets': { - description: - 'Total packets transferred in both directions.\nIf `source.packets` and `destination.packets` are known, `network.packets` is their sum.', - example: '24', - footnote: '', - group: 2, - level: 'core', - name: 'network.packets', - required: false, - type: 'long', - }, - 'network.protocol': { - description: 'L7 Network protocol name. ex. http, lumberjack, transport protocol', - example: 'http', - footnote: '', - group: 2, - level: 'core', - name: 'network.protocol', - required: false, - type: 'keyword', - }, - 'network.transport': { - description: - 'Same as network.iana_number, but instead using the Keyword name of the transport layer (UDP, TCP, IPv6-ICMP, etc.)', - example: 'TCP', - footnote: '', - group: 2, - level: 'core', - name: 'network.transport', - required: false, - type: 'keyword', - }, - 'network.type': { - description: - 'In the OSI Model this would be the Network Layer. IPv4, IPv6, IPSec, PIM, etc', - example: 'IPv4', - footnote: '', - group: 2, - level: 'core', - name: 'network.type', - required: false, - type: 'keyword', - }, - }, - group: 2, - name: 'network', - title: 'Network', - type: 'group', - }, - observer: { - description: - 'An observer is defined as a special network, security, or application device used to detect, observe, or create network, security, or application-related events and metrics. This could be a custom hardware appliance or a server that has been configured to run special network, security, or application software. Examples include firewalls, intrusion detection/prevention systems, network monitoring sensors, web application firewalls, data loss prevention systems, and APM servers. The observer.* fields shall be populated with details of the system, if any, that detects, observes and/or creates a network, security, or application event or metric. Message queues and ETL components used in processing events or metrics are not considered observers in ECS. \n', - fields: { - 'observer.hostname': { - description: 'Hostname of the observer.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'observer.hostname', - required: false, - type: 'keyword', - }, - 'observer.ip': { - description: 'IP address of the observer.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'observer.ip', - required: false, - type: 'ip', - }, - 'observer.mac': { - description: 'MAC address of the observer', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'observer.mac', - required: false, - type: 'keyword', - }, - 'observer.serial_number': { - description: 'Observer serial number.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'observer.serial_number', - required: false, - type: 'keyword', - }, - 'observer.type': { - description: - 'The type of the observer the data is coming from.\nThere is no predefined list of observer types. Some examples are `forwarder`, `firewall`, `ids`, `ips`, `proxy`, `poller`, `sensor`, `APM server`.', - example: 'firewall', - footnote: '', - group: 2, - level: 'core', - name: 'observer.type', - required: false, - type: 'keyword', - }, - 'observer.vendor': { - description: 'observer vendor information.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'observer.vendor', - required: false, - type: 'keyword', - }, - 'observer.version': { - description: 'Observer version.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'observer.version', - required: false, - type: 'keyword', - }, - }, - group: 2, - name: 'observer', - title: 'Observer', - type: 'group', - }, - organization: { - description: - 'The organization fields enrich data with information about the company or entity the data is associated with. These fields help you arrange or filter data stored in an index by one or multiple organizations.\n', - fields: { - 'organization.id': { - description: 'Unique identifier for the organization.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'organization.id', - required: false, - type: 'keyword', - }, - 'organization.name': { - description: 'Organization name.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'organization.name', - required: false, - type: 'keyword', - }, - }, - group: 2, - name: 'organization', - title: 'Organization', - type: 'group', - }, - os: { - description: 'The OS fields contain information about the operating system.\n', - fields: { - 'os.family': { - description: 'OS family (such as redhat, debian, freebsd, windows).', - example: 'debian', - footnote: '', - group: 2, - level: 'extended', - name: 'os.family', - required: false, - type: 'keyword', - }, - 'os.kernel': { - description: 'Operating system kernel version as a raw string.', - example: '4.4.0-112-generic', - footnote: '', - group: 2, - level: 'extended', - name: 'os.kernel', - required: false, - type: 'keyword', - }, - 'os.name': { - description: 'Operating system name.', - example: 'Mac OS X', - footnote: '', - group: 2, - level: 'extended', - name: 'os.name', - required: false, - type: 'keyword', - }, - 'os.platform': { - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - footnote: '', - group: 2, - level: 'extended', - name: 'os.platform', - required: false, - type: 'keyword', - }, - 'os.version': { - description: 'Operating system version as a raw string.', - example: '10.12.6-rc2', - footnote: '', - group: 2, - level: 'extended', - name: 'os.version', - required: false, - type: 'keyword', - }, - }, - group: 2, - name: 'os', - title: 'Operating System', - type: 'group', - }, - process: { - description: - 'These fields contain information about a process. These fields can help you correlate metrics information with a process id/name from a log message. The `process.pid` often stays in the metric itself and is copied to the global field for correlation.\n', - fields: { - 'process.args': { - description: 'Process arguments.\nMay be filtered to protect sensitive information.', - example: "['ssh', '-l', 'user', '10.0.0.16']", - footnote: '', - group: 2, - level: 'extended', - name: 'process.args', - required: false, - type: 'keyword', - }, - 'process.executable': { - description: 'Absolute path to the process executable.', - example: '/usr/bin/ssh', - footnote: '', - group: 2, - level: 'extended', - name: 'process.executable', - required: false, - type: 'keyword', - }, - 'process.name': { - description: 'Process name.\nSometimes called program name or similar.', - example: 'ssh', - footnote: '', - group: 2, - level: 'extended', - name: 'process.name', - required: false, - type: 'keyword', - }, - 'process.pid': { - description: 'Process id.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'process.pid', - required: false, - type: 'long', - }, - 'process.ppid': { - description: 'Process parent id.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'process.ppid', - required: false, - type: 'long', - }, - 'process.start': { - description: 'The time the process started.', - example: '2016-05-23T08:05:34.853Z', - footnote: '', - group: 2, - level: 'extended', - name: 'process.start', - required: false, - type: 'date', - }, - 'process.thread.id': { - description: 'Thread ID.', - example: '4242', - footnote: '', - group: 2, - level: 'extended', - name: 'process.thread.id', - required: false, - type: 'long', - }, - 'process.title': { description: - 'Process title.\nThe 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.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'process.title', - required: false, - type: 'keyword', - }, - 'process.working_directory': { - description: 'The working directory of the process.', - example: '/home/alice', - footnote: '', - group: 2, - level: 'extended', - name: 'process.working_directory', - required: false, - type: 'keyword', - }, - }, - group: 2, - name: 'process', - title: 'Process', - type: 'group', - }, - related: { - description: - 'This field set is meant to facilitate pivoting around a piece of data. Some pieces of information can be seen in many places in ECS. To facilitate searching for them, append values to their corresponding field in `related.`. A concrete example is IP addresses, which can be under host, observer, source, destination, client, server, and network.forwarded_ip. If you append all IPs to `related.ip`, you can then search for a given IP trivially, no matter where it appeared, by querying `related.ip:a.b.c.d`.\n', - fields: { - 'related.ip': { - description: 'All of the IPs seen on your event.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'related.ip', - required: false, - type: 'ip', - }, - }, - group: 2, - name: 'related', - title: 'Related', - type: 'group', - }, - server: { - description: - 'A Server is defined as the responder in a network connection for events regarding sessions, connections, or bidirectional flow records. For TCP events, the server is the receiver of the initial SYN packet(s) of the TCP connection. For other protocols, the server is generally the responder in the network transaction. Some systems actually use the term "responder" to refer the server in TCP connections. The server fields describe details about the system acting as the server in the network event. Server fields are usually populated in conjunction with client fields. Server fields are generally not populated for packet-level events.\n', - fields: { - 'server.bytes': { - description: 'Bytes sent from the server to the client.', - example: '184', - footnote: '', - group: 2, - level: 'core', - name: 'server.bytes', - required: false, - type: 'long', - }, - 'server.domain': { - description: 'Server domain.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'server.domain', - required: false, - type: 'keyword', - }, - 'server.ip': { - description: 'IP address of the server.\nCan be one or multiple IPv4 or IPv6 addresses.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'server.ip', - required: false, - type: 'ip', - }, - 'server.mac': { - description: 'MAC address of the server.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'server.mac', - required: false, - type: 'keyword', - }, - 'server.packets': { - description: 'Packets sent from the server to the client.', - example: '12', - footnote: '', - group: 2, - level: 'core', - name: 'server.packets', - required: false, - type: 'long', - }, - 'server.port': { - description: 'Port of the server.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'server.port', - required: false, - type: 'long', - }, - }, - group: 2, - name: 'server', - title: 'Server', - type: 'group', - }, - service: { - description: - 'The service fields describe the service for or from which the data was collected. These fields help you find and correlate logs for a specific service and version.\n', - fields: { - 'service.ephemeral_id': { - description: - 'Ephemeral identifier of this service (if one exists).\nThis id normally changes across restarts, but `service.id` does not.', - example: '8a4f500f', - footnote: '', - group: 2, - level: 'extended', - name: 'service.ephemeral_id', - required: false, - type: 'keyword', - }, - 'service.id': { - description: - 'Unique identifier of the running service.\nThis id should uniquely identify this service. This makes it possible to correlate logs and metrics for one specific service.\nExample: If you are experiencing issues with one redis instance, you can filter on that id to see metrics and logs for that single instance.', - example: 'd37e5ebfe0ae6c4972dbe9f0174a1637bb8247f6', - footnote: '', - group: 2, - level: 'core', - name: 'service.id', - required: false, - type: 'keyword', - }, - 'service.name': { - description: - 'Name of the service data is collected from.\nThe name of the service is normally user given. This allows if two instances of the same service are running on the same machine they can be differentiated by the `service.name`.\nAlso it allows for distributed services that run on multiple hosts to correlate the related instances based on the name.\nIn 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', - footnote: '', - group: 2, - level: 'core', - name: 'service.name', - required: false, - type: 'keyword', - }, - 'service.state': { - description: 'Current state of the service.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'service.state', - required: false, - type: 'keyword', - }, - 'service.type': { - description: - 'The type of the service data is collected from.\nThe type can be used to group and correlate logs and metrics from one service type.\nExample: If logs or metrics are collected from Elasticsearch, `service.type` would be `elasticsearch`.', - example: 'elasticsearch', - footnote: '', - group: 2, - level: 'core', - name: 'service.type', - required: false, - type: 'keyword', - }, - 'service.version': { - description: - 'Version of the service the data was collected from.\nThis allows to look at a data set only for a specific version of a service.', - example: '3.2.4', - footnote: '', - group: 2, - level: 'core', - name: 'service.version', - required: false, - type: 'keyword', - }, - }, - group: 2, - name: 'service', - title: 'Service', - type: 'group', - }, - source: { - description: - 'Source fields describe details about the source of a packet/event. Source fields are usually populated in conjunction with destination fields.\n', - fields: { - 'source.bytes': { - description: 'Bytes sent from the source to the destination.', - example: '184', - footnote: '', - group: 2, - level: 'core', - name: 'source.bytes', - required: false, - type: 'long', - }, - 'source.domain': { - description: 'Source domain.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'source.domain', - required: false, - type: 'keyword', - }, - 'source.ip': { - description: 'IP address of the source.\nCan be one or multiple IPv4 or IPv6 addresses.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'source.ip', - required: false, - type: 'ip', - }, - 'source.mac': { - description: 'MAC address of the source.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'source.mac', - required: false, - type: 'keyword', - }, - 'source.packets': { - description: 'Packets sent from the source to the destination.', - example: '12', - footnote: '', - group: 2, - level: 'core', - name: 'source.packets', - required: false, - type: 'long', - }, - 'source.port': { - description: 'Port of the source.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'source.port', - required: false, - type: 'long', - }, - }, - group: 2, - name: 'source', - title: 'Source', - type: 'group', - }, - url: { - description: 'URL fields provide a complete URL, with scheme, host, and path.\n', - fields: { - 'url.domain': { - description: - 'Domain of the request, such as "www.elastic.co".\nIn 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', - footnote: '', - group: 2, - level: 'extended', - name: 'url.domain', - required: false, - type: 'keyword', - }, - 'url.fragment': { - description: - 'Portion of the url after the `#`, such as "top".\nThe `#` is not part of the fragment.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'url.fragment', - required: false, - type: 'keyword', - }, - 'url.full': { - 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', - footnote: '', - group: 2, - level: 'extended', - name: 'url.full', - required: false, - type: 'keyword', - }, - 'url.original': { - description: - 'Unmodified original url as seen in the event source.\nNote 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.\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', - footnote: '', - group: 2, - level: 'extended', - name: 'url.original', - required: false, - type: 'keyword', - }, - 'url.password': { - description: 'Password of the request.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'url.password', - required: false, - type: 'keyword', - }, - 'url.path': { - description: 'Path of the request, such as "/search".', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'url.path', - required: false, - type: 'keyword', - }, - 'url.port': { - description: 'Port of the request, such as 443.', - example: '443', - footnote: '', - group: 2, - level: 'extended', - name: 'url.port', - required: false, - type: 'integer', - }, - 'url.query': { - description: - 'The query field describes the query string of the request, such as "q=elasticsearch".\nThe `?` 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.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'url.query', - required: false, - type: 'keyword', - }, - 'url.scheme': { - description: - 'Scheme of the request, such as "https".\nNote: The `:` is not part of the scheme.', - example: 'https', - footnote: '', - group: 2, - level: 'extended', - name: 'url.scheme', - required: false, - type: 'keyword', - }, - 'url.username': { - description: 'Username of the request.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'url.username', - required: false, - type: 'keyword', - }, - }, - group: 2, - name: 'url', - title: 'URL', - type: 'group', - }, - user: { - description: - 'The user fields describe information about the user that is relevant to the event. Fields can have one entry or multiple entries. If a user has more than one id, provide an array that includes all of them.\n', - fields: { - 'user.email': { - description: 'User email address.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'user.email', - required: false, - type: 'keyword', - }, - 'user.full_name': { - description: "User's full name, if available.", - example: 'Albert Einstein', - footnote: '', - group: 2, - level: 'extended', - name: 'user.full_name', - required: false, - type: 'keyword', - }, - 'user.group': { - description: - 'Group the user is a part of. This field can contain a list of groups, if necessary.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'user.group', - required: false, - type: 'keyword', - }, - 'user.hash': { - description: - 'Unique user hash to correlate information for a user in anonymized form.\nUseful if `user.id` or `user.name` contain confidential information and cannot be used.', - example: '', - footnote: '', - group: 2, - level: 'extended', - name: 'user.hash', - required: false, - type: 'keyword', - }, - 'user.id': { - description: 'One or multiple unique identifiers of the user.', - example: '', - footnote: '', - group: 2, - level: 'core', - name: 'user.id', - required: false, - type: 'keyword', - }, - 'user.name': { - description: 'Short name or login of the user.', - example: 'albert', - footnote: '', - group: 2, - level: 'core', - name: 'user.name', - required: false, - type: 'keyword', - }, - }, - group: 2, - name: 'user', - title: 'User', - type: 'group', - }, - user_agent: { - description: - 'The user_agent fields normally come from a browser request. They often show up in web service logs coming from the parsed user agent string.\n', - fields: { - 'user_agent.device.name': { - description: 'Name of the device.', - example: 'iPhone', - footnote: '', - group: 2, - level: 'extended', - name: 'user_agent.device.name', - required: false, - type: 'keyword', - }, - 'user_agent.name': { - description: 'Name of the user agent.', - example: 'Safari', - footnote: '', - group: 2, - level: 'extended', - name: 'user_agent.name', - required: false, - type: 'keyword', - }, - 'user_agent.original': { - 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 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1', - footnote: '', - group: 2, - level: 'extended', - name: 'user_agent.original', - required: false, - type: '(not indexed)', - }, - 'user_agent.version': { - description: 'Version of the user agent.', - example: '12.0', - footnote: '', - group: 2, - level: 'extended', - name: 'user_agent.version', - required: false, - type: 'keyword', - }, - }, - group: 2, - name: 'user_agent', - title: 'User agent', - type: 'group', + '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/legacy/plugins/siem/server/utils/beat_schema/8.0.0/filebeat.ts b/x-pack/legacy/plugins/siem/server/utils/beat_schema/8.0.0/filebeat.ts index a5877f6c34b8f..3b8c92ebba269 100644 --- a/x-pack/legacy/plugins/siem/server/utils/beat_schema/8.0.0/filebeat.ts +++ b/x-pack/legacy/plugins/siem/server/utils/beat_schema/8.0.0/filebeat.ts @@ -15,91 +15,129 @@ export const filebeatSchema: Schema = [ { key: 'ecs', title: 'ECS', - description: 'ECS fields.', + description: 'ECS Fields.', fields: [ { name: '@timestamp', - type: 'date', level: 'core', required: true, - example: '2016-05-23T08:05:34.853Z', + type: 'date', 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.', - }, - { - name: 'tags', - level: 'core', - type: 'keyword', - example: '["production", "env2"]', - description: 'List of keywords used to tag each event.', + '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', - example: { - env: 'production', - application: 'foo-bar', - }, + object_type: 'keyword', description: - '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.', + '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', - example: 'Hello World', description: - 'For log events the message field contains the log message. In other use cases the message field can be used to concatenate different values which are then freely searchable. If multiple messages exist, they can be combined into one message.', + '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 any, that collects, detects, or observes events on a host, or takes measurements on a host. Examples include Beats. Agents may also run on observers. ECS agent.* fields shall be populated with details of the agent running on the host or observer where the event happened or the measurement was taken.', + '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. For APM, it is the agent running in the app/service. The agent information does not change if data is sent through queuing systems like Kafka, Redis, or processing systems such as Logstash or APM Server.', + '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: 'version', + 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', - description: 'Version of the agent.', - example: '6.0.0-rc2', + 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: - '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.', + '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. 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.', + '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: 'id', + 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 identifier of this agent (if one exists). Example: For Beats this would be beat.id.', - example: '8a4f500d', + 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', + example: 15169, }, { - name: 'ephemeral_id', + name: 'organization.name', level: 'extended', type: 'keyword', - description: - 'Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but `agent.id` does not.', - example: '8a4f500f', + ignore_above: 1024, + multi_fields: [ + { + name: 'text', + type: 'text', + norms: false, + default_field: false, + }, + ], + description: 'Organization name.', + example: 'Google LLC', }, ], }, @@ -108,7183 +146,18212 @@ export const filebeatSchema: Schema = [ title: 'Client', group: 2, description: - 'A client is defined as the initiator of a network connection for events regarding sessions, connections, or bidirectional flow records. For TCP events, the client is the initiator of the TCP connection that sends the SYN packet(s). For other protocols, the client is generally the initiator or requestor in the network transaction. Some systems use the term "originator" to refer the client in TCP connections. The client fields describe details about the system acting as the client in the network event. Client fields are usually populated in conjunction with server fields. Client fields are generally not populated for packet-level events. Client / server representations can add semantic context to an exchange, which is helpful to visualize the data in certain situations. If your context falls in that category, you should still ensure that source and destination are filled appropriately.', + '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 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.', + '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: 'ip', - level: 'core', - type: 'ip', - description: 'IP address of the client. Can be one or multiple IPv4 or IPv6 addresses.', + 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: 'port', + 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', - description: 'Port of the client.', + format: 'bytes', + description: 'Bytes sent from the client to the server.', + example: 184, }, { - name: 'mac', + name: 'domain', level: 'core', type: 'keyword', - description: 'MAC address of the client.', + ignore_above: 1024, + description: 'Client domain.', }, { - name: 'domain', + name: 'geo.city_name', level: 'core', type: 'keyword', - description: 'Client domain.', + ignore_above: 1024, + description: 'City name.', + example: 'Montreal', }, { - name: 'bytes', + name: 'geo.continent_name', level: 'core', - type: 'long', - format: 'bytes', - example: 184, - description: 'Bytes sent from the client to the server.', + type: 'keyword', + ignore_above: 1024, + description: 'Name of the continent.', + example: 'North America', }, { - name: 'packets', + name: 'geo.country_iso_code', level: 'core', - type: 'long', - example: 12, - description: 'Packets sent from the client to the server.', + type: 'keyword', + ignore_above: 1024, + description: 'Country ISO code.', + example: 'CA', }, { - name: 'geo', - title: 'Geo', - group: 2, - description: - 'Geo fields can carry data about a specific location related to an event or geo information derived from an IP field.', - type: 'group', - fields: [ - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - 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.country_name', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Country name.', + example: 'Canada', }, - ], - }, - { - name: 'cloud', - title: 'Cloud', - group: 2, - description: 'Fields related to the cloud or infrastructure the events are coming from.', - footnote: - 'Examples: If Metricbeat is running on an EC2 host and fetches data from its host, the cloud info contains the data about this machine. If Metricbeat runs on a remote machine outside the cloud and fetches data from a service running in the cloud, the field contains cloud data from the machine the service is running on.', - type: 'group', - fields: [ { - name: 'provider', + name: 'geo.location', + level: 'core', + type: 'geo_point', + description: 'Longitude and latitude.', + example: '{ "lon": -73.614830, "lat": 45.505918 }', + }, + { + name: 'geo.name', level: 'extended', - example: 'ec2', type: 'keyword', + ignore_above: 1024, description: - 'Name of the cloud provider. Example values are ec2, gce, or digitalocean.', + '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: 'availability_zone', - level: 'extended', - example: 'us-east-1c', + name: 'geo.region_iso_code', + level: 'core', type: 'keyword', - description: 'Availability zone in which this host is running.', + ignore_above: 1024, + description: 'Region ISO code.', + example: 'CA-QC', }, { - name: 'region', - level: 'extended', + name: 'geo.region_name', + level: 'core', type: 'keyword', - example: 'us-east-1', - description: 'Region in which this host is running.', + ignore_above: 1024, + description: 'Region name.', + example: 'Quebec', }, { - name: 'instance.id', - level: 'extended', + 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', - example: 'i-1234567890abcdef0', - description: 'Instance ID of the host machine.', + ignore_above: 1024, + description: 'MAC address of the client.', }, { - name: 'instance.name', + name: 'nat.ip', level: 'extended', - type: 'keyword', - description: 'Instance name of the host machine.', + 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: 'machine.type', + 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', - example: 't2.medium', - description: 'Machine type of the host machine.', + 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: 'account.id', + name: 'top_level_domain', level: 'extended', type: 'keyword', - example: 666777888999, + ignore_above: 1024, 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.', + '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: 'container', - title: 'Container', - group: 2, - description: - 'Container fields are used for meta information about the specific container that is the source of information. These fields help correlate data based containers from any runtime.', - type: 'group', - fields: [ { - name: 'runtime', + name: 'user.domain', level: 'extended', type: 'keyword', - description: 'Runtime managing this container.', - example: 'docker', + 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: 'id', - level: 'core', + name: 'user.email', + level: 'extended', type: 'keyword', - description: 'Unique container id.', + ignore_above: 1024, + description: 'User email address.', }, { - name: 'image.name', + name: 'user.full_name', level: 'extended', type: 'keyword', - description: 'Name of the image the container was built on.', + 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: 'image.tag', + name: 'user.group.domain', level: 'extended', type: 'keyword', - description: 'Container image tag.', + 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: 'name', + name: 'user.group.id', level: 'extended', type: 'keyword', - description: 'Container name.', + ignore_above: 1024, + description: 'Unique identifier for the group on the system/platform.', }, { - name: 'labels', + name: 'user.group.name', level: 'extended', - type: 'object', - object_type: 'keyword', - description: 'Image labels.', + type: 'keyword', + ignore_above: 1024, + description: 'Name of the group.', }, - ], - }, - { - name: 'destination', - title: 'Destination', - group: 2, - description: - 'Destination fields describe details about the destination of a packet/event. Destination fields are usually populated in conjunction with source fields.', - type: 'group', - fields: [ { - name: 'address', + name: 'user.hash', level: 'extended', type: 'keyword', + ignore_above: 1024, 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.', + '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: 'ip', + name: 'user.id', level: 'core', - type: 'ip', - description: - 'IP address of the destination. Can be one or multiple IPv4 or IPv6 addresses.', + type: 'keyword', + ignore_above: 1024, + description: 'Unique identifiers of the user.', }, { - name: 'port', + name: 'user.name', level: 'core', - type: 'long', - description: 'Port of the destination.', + 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: 'mac', - level: 'core', + name: 'account.id', + level: 'extended', type: 'keyword', - description: 'MAC address of the destination.', + 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: 'domain', - level: 'core', + name: 'availability_zone', + level: 'extended', type: 'keyword', - description: 'Destination domain.', + ignore_above: 1024, + description: 'Availability zone in which this host is running.', + example: 'us-east-1c', }, { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - example: 184, - description: 'Bytes sent from the destination to the source.', + name: 'instance.id', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Instance ID of the host machine.', + example: 'i-1234567890abcdef0', }, { - name: 'packets', - level: 'core', - type: 'long', - example: 12, - description: 'Packets sent from the destination to the source.', + 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: 'geo', - title: 'Geo', - group: 2, + name: 'provider', + level: 'extended', + type: 'keyword', + ignore_above: 1024, description: - 'Geo fields can carry data about a specific location related to an event or geo information derived from an IP field.', - type: 'group', - fields: [ - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - 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 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: 'ecs', - title: 'ECS', + name: 'code_signature', + title: 'Code Signature', group: 2, - description: 'Meta-information specific to ECS.', + description: 'These fields contain information about binary code signatures.', type: 'group', fields: [ { - name: 'version', + 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', - required: true, + 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: - '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. The current version is 1.0.0-beta2 .', - example: '1.0.0-beta2', + '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: 'error', - title: 'Error', + name: 'container', + title: 'Container', group: 2, description: - 'These fields can represent errors of any kind. Use them for errors that happen while fetching events or in cases where the event itself contains an error.', + '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 identifier for the error.', + ignore_above: 1024, + description: 'Unique container id.', }, { - name: 'message', - level: 'core', - type: 'text', - description: 'Error message.', + name: 'image.name', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Name of the image the container was built on.', }, { - name: 'code', - level: 'core', + name: 'image.tag', + level: 'extended', type: 'keyword', - description: 'Error code describing the error.', + 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: 'event', - title: 'Event', + name: 'destination', + title: 'Destination', group: 2, description: - 'The event fields are used for context information about the log or metric event itself. A log is defined as an event containing details of something that happened. Log events must include the time at which the thing happened. Examples of log events include a process starting on a host, a network packet being sent from a source to a destination, or a network connection between a client and a server being initiated or closed. A metric is defined as an event containing one or more numerical or categorical measurements and the time at which the measurement was taken. Examples of metric events include memory pressure measured on a host, or vulnerabilities measured on a scanned host.', + '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: 'id', - level: 'core', + name: 'address', + level: 'extended', type: 'keyword', - description: 'Unique ID to describe the event.', - example: '8a4f500d', + 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: 'kind', + name: 'as.number', level: 'extended', - type: 'keyword', + type: 'long', description: - 'The kind of the event. This gives information about what type of information the event contains, without being specific to the contents of the event. Examples are `event`, `state`, `alarm`. Warning: In future versions of ECS, we plan to provide a list of acceptable values for this field, please use with caution.', - example: 'state', + 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', + example: 15169, }, { - name: 'category', - level: 'core', + name: 'as.organization.name', + level: 'extended', type: 'keyword', - description: - 'Event category. This contains high-level information about the contents of the event. It is more generic than `event.action`, in the sense that typically a category contains multiple actions. Warning: In future versions of ECS, we plan to provide a list of acceptable values for this field, please use with caution.', - example: 'user-management', + ignore_above: 1024, + multi_fields: [ + { + name: 'text', + type: 'text', + norms: false, + default_field: false, + }, + ], + description: 'Organization name.', + example: 'Google LLC', }, { - name: 'action', + name: 'bytes', level: 'core', - type: 'keyword', - 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', + type: 'long', + format: 'bytes', + description: 'Bytes sent from the destination to the source.', + example: 184, }, { - name: 'outcome', - level: 'extended', + name: 'domain', + level: 'core', type: 'keyword', - description: - 'The outcome of the event. If the event describes an action, this fields contains the outcome of that action. Examples outcomes are `success` and `failure`. Warning: In future versions of ECS, we plan to provide a list of acceptable values for this field, please use with caution.', - example: 'success', + ignore_above: 1024, + description: 'Destination domain.', }, { - name: 'type', + name: 'geo.city_name', level: 'core', type: 'keyword', - description: 'Reserved for future usage. Please avoid using this field for user data.', + ignore_above: 1024, + description: 'City name.', + example: 'Montreal', }, { - name: 'module', + name: 'geo.continent_name', level: 'core', type: 'keyword', - description: - 'Name of the module this data is coming from. This information is coming from the modules used in Beats or Logstash.', - example: 'mysql', + ignore_above: 1024, + description: 'Name of the continent.', + example: 'North America', }, { - name: 'dataset', + name: 'geo.country_iso_code', level: 'core', type: 'keyword', - description: - 'Name of the dataset. The concept of a `dataset` (fileset / metricset) is used in Beats as a subset of modules. It contains the information which is currently stored in metricset.name and metricset.module or fileset.name.', - example: 'stats', + ignore_above: 1024, + description: 'Country ISO code.', + example: 'CA', }, { - name: 'severity', + name: 'geo.country_name', level: 'core', - type: 'long', - example: '7', - description: - "Severity describes the severity of the event. What the different severity values mean can very different between use cases. It's up to the implementer to make sure severities are consistent across events. ", + type: 'keyword', + ignore_above: 1024, + description: 'Country name.', + example: 'Canada', }, { - name: 'original', + name: 'geo.location', level: 'core', - type: 'keyword', - 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', - 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`.', - index: false, - doc_values: false, + type: 'geo_point', + description: 'Longitude and latitude.', + example: '{ "lon": -73.614830, "lat": 45.505918 }', }, { - name: 'hash', + name: 'geo.name', level: 'extended', type: 'keyword', - example: '123456789012345678901234567890ABCD', + ignore_above: 1024, description: - 'Hash (perhaps logstash fingerprint) of raw field to be able to demonstrate log integrity.', + '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: 'duration', + name: 'geo.region_iso_code', level: 'core', - type: 'long', - format: 'duration', - input_format: 'nanoseconds', - 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.', + type: 'keyword', + ignore_above: 1024, + description: 'Region ISO code.', + example: 'CA-QC', }, { - name: 'timezone', - level: 'extended', + name: 'geo.region_name', + level: 'core', type: 'keyword', - 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").', + ignore_above: 1024, + description: 'Region name.', + example: 'Quebec', }, { - name: 'created', + name: 'ip', level: 'core', - type: 'date', + type: 'ip', description: - 'event.created contains the date when the event was created. This timestamp is distinct from @timestamp in that @timestamp contains the processed timestamp. For logs these two timestamps can be different as the timestamp in the log line and when the event is read for example by Filebeat are not identical. `@timestamp` must contain the timestamp extracted from the log line, event.created when the log line is read. The same could apply to package capturing where @timestamp contains the timestamp extracted from the network package and event.created when the event was created. In case the two timestamps are identical, @timestamp should be used.', + 'IP address of the destination.\n\nCan be one or multiple IPv4 or IPv6 addresses.', }, { - name: 'start', + name: 'mac', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'MAC address of the destination.', + }, + { + name: 'nat.ip', level: 'extended', - type: 'date', + type: 'ip', description: - 'event.start contains the date when the event started or when the activity was first observed.', + 'Translated ip of destination based NAT sessions (e.g. internet\nto private DMZ)\n\nTypically used with load balancers, firewalls, or routers.', }, { - name: 'end', + name: 'nat.port', level: 'extended', - type: 'date', + type: 'long', + format: 'string', description: - 'event.end contains the date when the event ended or when the activity was last observed.', + 'Port the source session is translated to by NAT Device.\n\nTypically used with load balancers, firewalls, or routers.', }, { - name: 'risk_score', + name: 'packets', level: 'core', - type: 'float', - description: - "Risk score or priority of the event (e.g. security solutions). Use your system's original value here. ", + type: 'long', + description: 'Packets sent from the destination to the source.', + example: 12, }, { - name: 'risk_score_norm', + name: 'port', + level: 'core', + type: 'long', + format: 'string', + description: 'Port of the destination.', + }, + { + name: 'registered_domain', level: 'extended', - type: 'float', + type: 'keyword', + ignore_above: 1024, 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.', + '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: 'file', - group: 2, - title: 'File', - description: - 'A file is defined as a set of information that has been created on, or has existed on a filesystem. File objects can be associated with host events, network events, and/or file events (e.g., those produced by File Integrity Monitoring [FIM] products or services). File fields provide details about the affected file associated with the event or metric.', - type: 'group', - fields: [ { - name: 'path', + name: 'top_level_domain', level: 'extended', type: 'keyword', - description: 'Path to the file.', + 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: 'target_path', + name: 'user.domain', level: 'extended', type: 'keyword', - description: 'Target path for symlinks.', + 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: 'extension', + name: 'user.email', level: 'extended', type: 'keyword', - description: 'File extension. This should allow easy filtering by file extensions.', - example: 'png', + ignore_above: 1024, + description: 'User email address.', }, { - name: 'type', + name: 'user.full_name', level: 'extended', type: 'keyword', - description: 'File type (file, dir, or symlink).', + 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: 'device', + name: 'user.group.domain', level: 'extended', type: 'keyword', - description: 'Device that is the source of the file.', + 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: 'inode', + name: 'user.group.id', level: 'extended', type: 'keyword', - description: 'Inode representing the file in the filesystem.', + ignore_above: 1024, + description: 'Unique identifier for the group on the system/platform.', }, { - name: 'uid', + name: 'user.group.name', level: 'extended', type: 'keyword', - description: 'The user ID (UID) or security identifier (SID) of the file owner.', + ignore_above: 1024, + description: 'Name of the group.', }, { - name: 'owner', + name: 'user.hash', level: 'extended', type: 'keyword', - description: "File owner's username.", + 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: 'gid', - level: 'extended', + name: 'user.id', + level: 'core', type: 'keyword', - description: 'Primary group ID (GID) of the file.', + ignore_above: 1024, + description: 'Unique identifiers of the user.', }, { - name: 'group', - level: 'extended', + name: 'user.name', + level: 'core', type: 'keyword', - description: 'Primary group name of the file.', + 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: 'mode', + name: 'code_signature.status', level: 'extended', type: 'keyword', - example: 416, - description: 'Mode of the file in octal representation.', + 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: 'size', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'File size in bytes (field is only added when `type` is `file`).', + 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: 'mtime', + name: 'code_signature.trusted', level: 'extended', - type: 'date', - description: 'Last time file content was modified.', + 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: 'ctime', + name: 'code_signature.valid', level: 'extended', - type: 'date', - description: 'Last time file metadata changed.', + 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: 'group', - title: 'Group', - group: 2, - description: - 'The group fields are meant to represent groups that are relevant to the event.', - type: 'group', - fields: [ { - name: 'id', + name: 'hash.md5', level: 'extended', type: 'keyword', - description: 'Unique identifier for the group on the system/platform.', + ignore_above: 1024, + description: 'MD5 hash.', + default_field: false, }, { - name: 'name', + name: 'hash.sha1', level: 'extended', type: 'keyword', - description: 'Name of the group.', + ignore_above: 1024, + description: 'SHA1 hash.', + default_field: false, }, - ], - }, - { - name: 'host', - title: 'Host', - group: 2, - description: - 'A host is defined as a general computing instance. ECS host.* fields should be populated with details about the host on which the event happened, or on which the measurement was taken. Host types include hardware, virtual machines, Docker containers, and Kubernetes nodes.', - type: 'group', - fields: [ { - name: 'hostname', - level: 'core', + name: 'hash.sha256', + level: 'extended', type: 'keyword', - description: - 'Hostname of the host. It normally contains what the `hostname` command returns on the host machine.', + ignore_above: 1024, + description: 'SHA256 hash.', + default_field: false, }, { - name: 'name', - level: 'core', + name: 'hash.sha512', + level: 'extended', type: 'keyword', - 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.', + ignore_above: 1024, + description: 'SHA512 hash.', + default_field: false, }, { - name: 'id', + name: 'name', level: 'core', type: 'keyword', + ignore_above: 1024, 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 of the library.\n\nThis generally maps to the name of the file on disk.', + example: 'kernel32.dll', + default_field: false, }, { - name: 'ip', - level: 'core', - type: 'ip', - description: 'Host ip address.', + 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: 'mac', - level: 'core', + name: 'pe.company', + level: 'extended', type: 'keyword', - description: 'Host mac address.', + ignore_above: 1024, + description: 'Internal company name of the file, provided at compile-time.', + example: 'Microsoft Corporation', + default_field: false, }, { - name: 'type', - level: 'core', + name: 'pe.description', + level: 'extended', type: 'keyword', - 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.', + ignore_above: 1024, + description: 'Internal description of the file, provided at compile-time.', + example: 'Paint', + default_field: false, }, { - name: 'architecture', - level: 'core', + name: 'pe.file_version', + level: 'extended', type: 'keyword', - example: 'x86_64', - description: 'Operating system architecture.', + ignore_above: 1024, + description: 'Internal version of the file, provided at compile-time.', + example: '6.3.9600.17415', + default_field: false, }, { - name: 'os', - title: 'Operating System', - group: 2, - description: 'The OS fields contain information about the operating system.', - reusable: { - top_level: false, - expected: ['observer', 'host', 'user_agent'], - }, - type: 'group', - fields: [ - { - name: 'platform', - level: 'extended', - type: 'keyword', - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - example: 'Mac OS X', - description: 'Operating system name, without the version.', - }, - { - name: 'full', - level: 'extended', - type: 'keyword', - example: 'Mac OS Mojave', - description: 'Operating system name, including the version or code name.', - }, - { - name: 'family', - level: 'extended', - type: 'keyword', - example: 'debian', - description: 'OS family (such as redhat, debian, freebsd, windows).', - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - example: '10.14.1', - description: 'Operating system version as a raw string.', - }, - { - name: 'kernel', - level: 'extended', - type: 'keyword', - example: '4.4.0-112-generic', - description: 'Operating system kernel version as a raw string.', - }, - ], + 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: 'geo', - title: 'Geo', - group: 2, - description: - 'Geo fields can carry data about a specific location related to an event or geo information derived from an IP field.', - type: 'group', - fields: [ - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - 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: '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: 'http', - title: 'HTTP', + name: 'dns', + title: 'DNS', group: 2, - description: 'Fields related to HTTP activity.', + 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: 'request.method', + name: 'answers', level: 'extended', - type: 'keyword', + type: 'object', + object_type: 'keyword', description: - 'Http request method. The field value must be normalized to lowercase for querying. See "Lowercase Capitalization" in the "Implementing ECS" section.', - example: 'get, post, put', + '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: 'request.body.content', + name: 'answers.class', level: 'extended', type: 'keyword', - description: 'The full http request body.', - example: 'Hello world', + ignore_above: 1024, + description: 'The class of DNS data contained in this resource record.', + example: 'IN', }, { - name: 'request.referrer', + name: 'answers.data', level: 'extended', type: 'keyword', - description: 'Referrer for this HTTP request.', - example: 'https://blog.example.com/', + 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: 'response.status_code', + 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: 'Http response status code.', - example: 404, + 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: 'response.body.content', + name: 'answers.type', level: 'extended', type: 'keyword', - description: 'The full http response body.', - example: 'Hello world', + ignore_above: 1024, + description: 'The type of data contained in this resource record.', + example: 'CNAME', }, { - name: 'version', + name: 'header_flags', level: 'extended', type: 'keyword', - description: 'Http version.', - example: 1.1, + 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: 'request.bytes', + name: 'id', level: 'extended', - type: 'long', - format: 'bytes', - description: 'Total size in bytes of the request (body and headers).', - example: 1437, + 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: 'request.body.bytes', + name: 'op_code', level: 'extended', - type: 'long', - format: 'bytes', - description: 'Size in bytes of the request body.', - example: 887, + 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: 'response.bytes', + name: 'question.class', level: 'extended', - type: 'long', - format: 'bytes', - description: 'Total size in bytes of the response (body and headers).', - example: 1437, + type: 'keyword', + ignore_above: 1024, + description: 'The class of records being queried.', + example: 'IN', }, { - name: 'response.body.bytes', + name: 'question.name', level: 'extended', - type: 'long', - format: 'bytes', - description: 'Size in bytes of the response body.', - example: 887, + 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: 'log', - title: 'Log', - description: 'Fields which are specific to log events.', + name: 'ecs', + title: 'ECS', + group: 2, + description: 'Meta-information specific to ECS.', type: 'group', fields: [ { - name: 'level', + 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', - description: 'Log level of the log event. Some examples are `WARN`, `ERR`, `INFO`.', - example: 'ERR', + ignore_above: 1024, + description: 'Error code describing the error.', }, { - name: 'original', + name: 'id', level: 'core', type: 'keyword', - example: 'Sep 19 08:26:10 localhost My log', - index: false, - doc_values: false, - 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`. ", + 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: 'network', - title: 'Network', + name: 'event', + title: 'Event', group: 2, description: - 'The network is defined as the communication path over which a host or network event happens. The network.* fields should be populated with details about the network activity associated with an event.', + '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: 'name', - level: 'extended', + name: 'action', + level: 'core', type: 'keyword', - description: 'Name given by operators to sections of their network.', - example: 'Guest Wifi', + 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: 'type', + name: 'category', level: 'core', type: 'keyword', + ignore_above: 1024, 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 "Lowercase Capitalization" in the "Implementing ECS" section.', - example: 'ipv4', + '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: 'iana_number', + name: 'code', level: 'extended', type: 'keyword', + ignore_above: 1024, 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, + '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: 'transport', + 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: - '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 "Lowercase Capitalization" in the "Implementing ECS" section.', - example: 'tcp', + '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: 'application', + 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: - 'A name given to an application. 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 "Lowercase Capitalization" in the "Implementing ECS" section.', - example: 'aim', + 'Hash (perhaps logstash fingerprint) of raw field to be able to\ndemonstrate log integrity.', + example: '123456789012345678901234567890ABCD', }, { - name: 'protocol', + 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: - 'L7 Network protocol name. ex. http, lumberjack, transport protocol. The field value must be normalized to lowercase for querying. See "Lowercase Capitalization" in the "Implementing ECS" section.', - example: 'http', + '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: 'direction', + name: 'kind', level: 'core', type: 'keyword', + ignore_above: 1024, 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', + '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: 'forwarded_ip', + name: 'module', level: 'core', - type: 'ip', - description: 'Host IP address when the source IP address is the proxy.', - example: '192.1.1.2', + 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: 'community_id', + 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: - '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=', + '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: 'bytes', + 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: 'bytes', + format: 'string', description: - 'Total bytes transferred in both directions. If `source.bytes` and `destination.bytes` are known, `network.bytes` is their sum.', - example: 368, + '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: 'packets', + name: 'severity', level: 'core', type: 'long', + format: 'string', description: - 'Total packets transferred in both directions. If `source.packets` and `destination.packets` are known, `network.packets` is their sum.', - example: 24, + '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: 'observer', - title: 'Observer', + name: 'file', + title: 'File', group: 2, description: - 'An observer is defined as a special network, security, or application device used to detect, observe, or create network, security, or application-related events and metrics. This could be a custom hardware appliance or a server that has been configured to run special network, security, or application software. Examples include firewalls, intrusion detection/prevention systems, network monitoring sensors, web application firewalls, data loss prevention systems, and APM servers. The observer.* fields shall be populated with details of the system, if any, that detects, observes and/or creates a network, security, or application event or metric. Message queues and ETL components used in processing events or metrics are not considered observers in ECS.', + '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: 'mac', - level: 'core', - type: 'keyword', - description: 'MAC address of the observer', + 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: 'ip', - level: 'core', - type: 'ip', - description: 'IP address of the observer.', + 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: 'hostname', + name: 'code_signature.exists', level: 'core', - type: 'keyword', - description: 'Hostname of the observer.', + type: 'boolean', + description: 'Boolean to capture if a signature is present.', + example: 'true', + default_field: false, }, { - name: 'vendor', - level: 'core', + name: 'code_signature.status', + level: 'extended', type: 'keyword', - description: 'observer vendor information.', + 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: 'version', + name: 'code_signature.subject_name', level: 'core', type: 'keyword', - description: 'Observer version.', + ignore_above: 1024, + description: 'Subject name of the code signer', + example: 'Microsoft Corporation', + default_field: false, }, { - name: 'serial_number', + name: 'code_signature.trusted', level: 'extended', - type: 'keyword', - description: 'Observer serial number.', - }, - { - name: 'type', - level: 'core', - type: 'keyword', + type: 'boolean', 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', + '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: 'os', - title: 'Operating System', - group: 2, - description: 'The OS fields contain information about the operating system.', - reusable: { - top_level: false, - expected: ['observer', 'host', 'user_agent'], - }, - type: 'group', - fields: [ - { - name: 'platform', - level: 'extended', - type: 'keyword', - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - example: 'Mac OS X', - description: 'Operating system name, without the version.', - }, - { - name: 'full', - level: 'extended', - type: 'keyword', - example: 'Mac OS Mojave', - description: 'Operating system name, including the version or code name.', - }, - { - name: 'family', - level: 'extended', - type: 'keyword', - example: 'debian', - description: 'OS family (such as redhat, debian, freebsd, windows).', - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - example: '10.14.1', - description: 'Operating system version as a raw string.', - }, - { - name: 'kernel', - level: 'extended', - type: 'keyword', - example: '4.4.0-112-generic', - description: 'Operating system kernel version as a raw string.', - }, - ], + 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: 'geo', - title: 'Geo', - group: 2, + name: 'created', + level: 'extended', + type: 'date', description: - 'Geo fields can carry data about a specific location related to an event or geo information derived from an IP field.', - type: 'group', - fields: [ - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - 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', - }, - ], + 'File creation time.\n\nNote that not all filesystems store the creation time.', }, - ], - }, - { - name: 'organization', - title: 'Organization', - group: 2, - description: - 'The organization fields enrich data with information about the company or entity the data is associated with. These fields help you arrange or filter data stored in an index by one or multiple organizations.', - type: 'group', - fields: [ { - name: 'name', + 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', - description: 'Organization name.', + ignore_above: 1024, + description: 'Device that is the source of the file.', + example: 'sda', }, { - name: 'id', + name: 'directory', level: 'extended', type: 'keyword', - description: 'Unique identifier for the organization.', + ignore_above: 1024, + description: + 'Directory where the file is located. It should include the drive\nletter, when appropriate.', + example: '/home/alice', }, - ], - }, - { - name: 'os', - title: 'Operating System', - group: 2, - description: 'The OS fields contain information about the operating system.', - reusable: { - top_level: false, - expected: ['observer', 'host', 'user_agent'], - }, - type: 'group', - fields: [ { - name: 'platform', + name: 'drive_letter', level: 'extended', type: 'keyword', - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', + 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: 'name', + name: 'extension', level: 'extended', type: 'keyword', - example: 'Mac OS X', - description: 'Operating system name, without the version.', + ignore_above: 1024, + description: 'File extension.', + example: 'png', }, { - name: 'full', + name: 'gid', level: 'extended', type: 'keyword', - example: 'Mac OS Mojave', - description: 'Operating system name, including the version or code name.', + ignore_above: 1024, + description: 'Primary group ID (GID) of the file.', + example: '1001', }, { - name: 'family', + name: 'group', level: 'extended', type: 'keyword', - example: 'debian', - description: 'OS family (such as redhat, debian, freebsd, windows).', + ignore_above: 1024, + description: 'Primary group name of the file.', + example: 'alice', }, { - name: 'version', + name: 'hash.md5', level: 'extended', type: 'keyword', - example: '10.14.1', - description: 'Operating system version as a raw string.', + ignore_above: 1024, + description: 'MD5 hash.', }, { - name: 'kernel', + name: 'hash.sha1', level: 'extended', type: 'keyword', - example: '4.4.0-112-generic', - description: 'Operating system kernel version as a raw string.', + ignore_above: 1024, + description: 'SHA1 hash.', }, - ], - }, - { - name: 'process', - title: 'Process', - group: 2, - description: - 'These fields contain information about a process. These fields can help you correlate metrics information with a process id/name from a log message. The `process.pid` often stays in the metric itself and is copied to the global field for correlation.', - type: 'group', - fields: [ { - name: 'pid', - level: 'core', - type: 'long', - description: 'Process id.', - example: 'ssh', + name: 'hash.sha256', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'SHA256 hash.', }, { - name: 'name', + name: 'hash.sha512', level: 'extended', type: 'keyword', - description: 'Process name. Sometimes called program name or similar.', - example: 'ssh', + ignore_above: 1024, + description: 'SHA512 hash.', }, { - name: 'ppid', + name: 'inode', level: 'extended', - type: 'long', - description: 'Process parent id.', + type: 'keyword', + ignore_above: 1024, + description: 'Inode representing the file in the filesystem.', + example: '256383', }, { - name: 'args', + name: 'mime_type', level: 'extended', type: 'keyword', - description: 'Process arguments. May be filtered to protect sensitive information.', - example: ['ssh', '-l', 'user', '10.0.0.16'], + 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: 'executable', + name: 'mode', level: 'extended', type: 'keyword', - description: 'Absolute path to the process executable.', - example: '/usr/bin/ssh', + ignore_above: 1024, + description: 'Mode of the file in octal representation.', + example: '0640', }, { - name: 'title', + 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: - '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.', + 'Full path to the file, including the file name. It should include\nthe drive letter, when appropriate.', + example: '/home/alice/example.png', }, { - name: 'thread.id', + 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', - example: 4242, - description: 'Thread ID.', + description: 'File size in bytes.\n\nOnly relevant when `file.type` is "file".', + example: 16384, }, { - name: 'start', + name: 'target_path', level: 'extended', - type: 'date', - example: '2016-05-23T08:05:34.853Z', - description: 'The time the process started.', + type: 'keyword', + ignore_above: 1024, + multi_fields: [ + { + name: 'text', + type: 'text', + norms: false, + default_field: false, + }, + ], + description: 'Target path for symlinks.', }, { - name: 'working_directory', + name: 'type', level: 'extended', type: 'keyword', - example: '/home/alice', - description: 'The working directory of the process.', + ignore_above: 1024, + description: 'File type (file, dir, or symlink).', + example: 'file', }, - ], - }, - { - name: 'related', - title: 'Related', - group: 2, - description: - 'This field set is meant to facilitate pivoting around a piece of data. Some pieces of information can be seen in many places in ECS. To facilitate searching for them, append values to their corresponding field in `related.`. A concrete example is IP addresses, which can be under host, observer, source, destination, client, server, and network.forwarded_ip. If you append all IPs to `related.ip`, you can then search for a given IP trivially, no matter where it appeared, by querying `related.ip:a.b.c.d`.', - type: 'group', - fields: [ { - name: 'ip', + name: 'uid', level: 'extended', - type: 'ip', - description: 'All of the IPs seen on your event.', + type: 'keyword', + ignore_above: 1024, + description: 'The user ID (UID) or security identifier (SID) of the file owner.', + example: '1001', }, ], }, { - name: 'server', - title: 'Server', + name: 'geo', + title: 'Geo', group: 2, description: - 'A Server is defined as the responder in a network connection for events regarding sessions, connections, or bidirectional flow records. For TCP events, the server is the receiver of the initial SYN packet(s) of the TCP connection. For other protocols, the server is generally the responder in the network transaction. Some systems actually use the term "responder" to refer the server in TCP connections. The server fields describe details about the system acting as the server in the network event. Server fields are usually populated in conjunction with client fields. Server fields are generally not populated for packet-level events. Client / server representations can add semantic context to an exchange, which is helpful to visualize the data in certain situations. If your context falls in that category, you should still ensure that source and destination are filled appropriately.', + '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: 'address', - level: 'extended', + name: 'city_name', + level: 'core', type: 'keyword', - 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.', + ignore_above: 1024, + description: 'City name.', + example: 'Montreal', }, { - name: 'ip', + name: 'continent_name', level: 'core', - type: 'ip', - description: 'IP address of the server. Can be one or multiple IPv4 or IPv6 addresses.', + type: 'keyword', + ignore_above: 1024, + description: 'Name of the continent.', + example: 'North America', }, { - name: 'port', + name: 'country_iso_code', level: 'core', - type: 'long', - description: 'Port of the server.', + type: 'keyword', + ignore_above: 1024, + description: 'Country ISO code.', + example: 'CA', }, { - name: 'mac', + name: 'country_name', level: 'core', type: 'keyword', - description: 'MAC address of the server.', + ignore_above: 1024, + description: 'Country name.', + example: 'Canada', }, { - name: 'domain', + name: 'location', level: 'core', - type: 'keyword', - description: 'Server domain.', + type: 'geo_point', + description: 'Longitude and latitude.', + example: '{ "lon": -73.614830, "lat": 45.505918 }', }, { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - example: 184, - description: 'Bytes sent from the server to the client.', + 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: 'packets', + name: 'region_iso_code', level: 'core', - type: 'long', - example: 12, - description: 'Packets sent from the server to the client.', + type: 'keyword', + ignore_above: 1024, + description: 'Region ISO code.', + example: 'CA-QC', }, { - name: 'geo', - title: 'Geo', - group: 2, - description: - 'Geo fields can carry data about a specific location related to an event or geo information derived from an IP field.', - type: 'group', - fields: [ - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - 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: 'region_name', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Region name.', + example: 'Quebec', }, ], }, { - name: 'service', - title: 'Service', + name: 'group', + title: 'Group', group: 2, description: - 'The service fields describe the service for or from which the data was collected. These fields help you find and correlate logs for a specific service and version.', + 'The group fields are meant to represent groups that are relevant\nto the event.', type: 'group', fields: [ { - name: 'id', - level: 'core', + name: 'domain', + level: 'extended', type: 'keyword', + ignore_above: 1024, description: - 'Unique identifier of the running service. This id should uniquely identify this service. This makes it possible to correlate logs and metrics for one specific service. Example: If you are experiencing issues with one redis instance, you can filter on that id to see metrics and logs for that single instance.', - example: 'd37e5ebfe0ae6c4972dbe9f0174a1637bb8247f6', + '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: 'core', + level: 'extended', type: 'keyword', - example: 'elasticsearch-metrics', - description: - 'Name of the service data is collected from. The name of the service is normally user given. This allows if two instances of the same service are running on the same machine they can be differentiated by the `service.name`. Also it 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.', + 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: 'type', - level: 'core', + name: 'md5', + level: 'extended', type: 'keyword', - example: 'elasticsearch', - 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`.', + ignore_above: 1024, + description: 'MD5 hash.', }, { - name: 'state', - level: 'core', + name: 'sha1', + level: 'extended', type: 'keyword', - description: 'Current state of the service.', + ignore_above: 1024, + description: 'SHA1 hash.', }, { - name: 'version', - level: 'core', + name: 'sha256', + level: 'extended', type: 'keyword', - example: '3.2.4', - 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.', + ignore_above: 1024, + description: 'SHA256 hash.', }, { - name: 'ephemeral_id', + name: 'sha512', level: 'extended', type: 'keyword', - description: - 'Ephemeral identifier of this service (if one exists). This id normally changes across restarts, but `service.id` does not.', - example: '8a4f500f', + ignore_above: 1024, + description: 'SHA512 hash.', }, ], }, { - name: 'source', - title: 'Source', + name: 'host', + title: 'Host', group: 2, description: - 'Source fields describe details about the source of a packet/event. Source fields are usually populated in conjunction with destination fields.', + '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: 'address', + 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: - '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 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: 'ip', + name: 'geo.city_name', level: 'core', - type: 'ip', - description: 'IP address of the source. Can be one or multiple IPv4 or IPv6 addresses.', + type: 'keyword', + ignore_above: 1024, + description: 'City name.', + example: 'Montreal', }, { - name: 'port', + name: 'geo.continent_name', level: 'core', - type: 'long', - description: 'Port of the source.', + type: 'keyword', + ignore_above: 1024, + description: 'Name of the continent.', + example: 'North America', }, { - name: 'mac', + name: 'geo.country_iso_code', level: 'core', type: 'keyword', - description: 'MAC address of the source.', + ignore_above: 1024, + description: 'Country ISO code.', + example: 'CA', }, { - name: 'domain', + name: 'geo.country_name', level: 'core', type: 'keyword', - description: 'Source domain.', + ignore_above: 1024, + description: 'Country name.', + example: 'Canada', }, { - name: 'bytes', + name: 'geo.location', level: 'core', - type: 'long', - format: 'bytes', - example: 184, - description: 'Bytes sent from the source to the destination.', + type: 'geo_point', + description: 'Longitude and latitude.', + example: '{ "lon": -73.614830, "lat": 45.505918 }', }, { - name: 'packets', + 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: 'long', - example: 12, - description: 'Packets sent from the source to the destination.', + type: 'keyword', + ignore_above: 1024, + description: 'Region ISO code.', + example: 'CA-QC', }, { - name: 'geo', - title: 'Geo', - group: 2, - description: - 'Geo fields can carry data about a specific location related to an event or geo information derived from an IP field.', - type: 'group', - fields: [ - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - 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.region_name', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Region name.', + example: 'Quebec', }, - ], - }, - { - name: 'url', - title: 'URL', - description: 'URL fields provide a complete URL, with scheme, host, and path.', - type: 'group', - fields: [ { - name: 'original', - level: 'extended', + name: 'hostname', + level: 'core', type: 'keyword', + ignore_above: 1024, 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', + 'Hostname of the host.\n\nIt normally contains what the `hostname` command returns on the host machine.', }, { - name: 'full', - level: 'extended', + name: 'id', + level: 'core', type: 'keyword', + ignore_above: 1024, 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', + '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: 'scheme', - level: 'extended', + name: 'ip', + level: 'core', + type: 'ip', + description: 'Host ip addresses.', + }, + { + name: 'mac', + level: 'core', type: 'keyword', - description: - 'Scheme of the request, such as "https". Note: The `:` is not part of the scheme.', - example: 'https', + ignore_above: 1024, + description: 'Host mac addresses.', }, { - name: 'domain', - level: 'extended', + name: 'name', + level: 'core', type: 'keyword', + ignore_above: 1024, description: - 'Domain of the request, 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 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: 'port', + name: 'os.family', level: 'extended', - type: 'integer', - description: 'Port of the request, such as 443.', - example: 443, + type: 'keyword', + ignore_above: 1024, + description: 'OS family (such as redhat, debian, freebsd, windows).', + example: 'debian', }, { - name: 'path', + name: 'os.full', level: 'extended', type: 'keyword', - description: 'Path of the request, such as "/search".', + 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: 'query', + name: 'os.kernel', level: 'extended', type: 'keyword', - 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.', + ignore_above: 1024, + description: 'Operating system kernel version as a raw string.', + example: '4.4.0-112-generic', }, { - name: 'fragment', + name: 'os.name', level: 'extended', type: 'keyword', - description: - 'Portion of the url after the `#`, such as "top". The `#` is not part of the fragment.', + 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: 'username', + name: 'os.platform', level: 'extended', type: 'keyword', - description: 'Username of the request.', + ignore_above: 1024, + description: 'Operating system platform (such centos, ubuntu, windows).', + example: 'darwin', }, { - name: 'password', + name: 'os.version', level: 'extended', type: 'keyword', - description: 'Password of the request.', + ignore_above: 1024, + description: 'Operating system version as a raw string.', + example: '10.14.1', }, - ], - }, - { - name: 'user', - title: 'User', - group: 2, - description: - 'The user fields describe information about the user that is relevant to the event. Fields can have one entry or multiple entries. If a user has more than one id, provide an array that includes all of them.', - reusable: { - top_level: true, - expected: ['client', 'destination', 'host', 'server', 'source'], - }, - type: 'group', - fields: [ { - name: 'id', + name: 'type', level: 'core', type: 'keyword', - description: 'One or multiple unique identifiers of the user.', + 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: 'name', - level: 'core', - type: 'keyword', - example: 'albert', - description: 'Short name or login of the user.', + name: 'uptime', + level: 'extended', + type: 'long', + description: 'Seconds the host has been up.', + example: 1325, }, { - name: 'full_name', + name: 'user.domain', level: 'extended', type: 'keyword', - example: 'Albert Einstein', - description: "User's full name, if available. ", + 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', + name: 'user.email', level: 'extended', type: 'keyword', + ignore_above: 1024, description: 'User email address.', }, { - name: 'hash', + 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: - '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 of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', }, { - name: 'group', - title: 'Group', - group: 2, + 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: - 'The group fields are meant to represent groups that are relevant to the event.', - type: 'group', - fields: [ - { - name: 'id', - level: 'extended', - type: 'keyword', - description: 'Unique identifier for the group on the system/platform.', - }, + '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: 'name', - level: 'extended', - type: 'keyword', - description: 'Name of the group.', + 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', + name: 'http', + title: 'HTTP', group: 2, description: - 'The user_agent fields normally come from a browser request. They often show up in web service logs coming from the parsed user agent string.', + 'Fields related to HTTP activity. Use the `url` field set to store\nthe url of the request.', type: 'group', fields: [ { - name: 'original', + name: 'request.body.bytes', 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 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1', + type: 'long', + format: 'bytes', + description: 'Size in bytes of the request body.', + example: 887, }, { - name: 'name', + name: 'request.body.content', level: 'extended', type: 'keyword', - example: 'Safari', - description: 'Name of the user agent.', + ignore_above: 1024, + multi_fields: [ + { + name: 'text', + type: 'text', + norms: false, + default_field: false, + }, + ], + description: 'The full HTTP request body.', + example: 'Hello world', }, { - name: 'version', + 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: 'Version of the user agent.', - example: 12, + 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: 'device.name', + name: 'request.referrer', level: 'extended', type: 'keyword', - example: 'iPhone', - description: 'Name of the device.', + ignore_above: 1024, + description: 'Referrer for this HTTP request.', + example: 'https://blog.example.com/', }, { - name: 'os', - title: 'Operating System', - group: 2, - description: 'The OS fields contain information about the operating system.', - reusable: { - top_level: false, - expected: ['observer', 'host', 'user_agent'], - }, - type: 'group', - fields: [ - { - name: 'platform', - level: 'extended', - type: 'keyword', - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - example: 'Mac OS X', - description: 'Operating system name, without the version.', - }, - { - name: 'full', - level: 'extended', - type: 'keyword', - example: 'Mac OS Mojave', - description: 'Operating system name, including the version or code name.', - }, - { - name: 'family', - level: 'extended', - type: 'keyword', - example: 'debian', - description: 'OS family (such as redhat, debian, freebsd, windows).', - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - example: '10.14.1', - description: 'Operating system version as a raw string.', - }, + 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: 'kernel', - level: 'extended', - type: 'keyword', - example: '4.4.0-112-generic', - description: 'Operating system kernel version as a raw string.', + 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: 'agent.hostname', - type: 'keyword', - description: 'Hostname of the agent.', - }, - ], - }, - { - key: 'beat', - title: 'Beat', - description: 'Contains common beat fields available in all event types.', - fields: [ - { - name: 'beat.timezone', - type: 'alias', - path: 'event.timezone', - migration: true, - }, - { - name: 'fields', - type: 'object', - object_type: 'keyword', - description: 'Contains user configurable fields.', - }, - { - name: 'error', + 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', - description: 'Error fields containing additional info in case of errors.', fields: [ { - name: 'type', + 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', - description: 'Error type.', + ignore_above: 1024, + description: 'Interface name as reported by the system.', + example: 'eth0', + default_field: false, }, ], }, { - name: 'beat.name', - type: 'alias', - path: 'host.name', - migration: true, - }, - { - name: 'beat.hostname', - type: 'alias', - path: 'agent.hostname', - migration: true, - }, - ], - }, - { - key: 'cloud', - title: 'Cloud provider metadata', - description: 'Metadata from cloud providers added by the add_cloud_metadata processor.', - fields: [ - { - name: 'cloud.project.id', - example: 'project-x', - description: 'Name of the project in Google Cloud.', - }, - { - 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.', - short_config: false, - anchor: 'docker-processor', - fields: [ - { - name: 'docker', + 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: 'container.id', - type: 'alias', - path: 'container.id', - migration: true, + 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: 'container.image', - type: 'alias', - path: 'container.image.name', - migration: true, + 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: 'container.name', - type: 'alias', - path: 'container.name', - migration: true, + 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: 'container.labels', + 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: 'Image labels.', + 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', }, ], }, - ], - }, - { - key: 'host', - title: 'Host', - description: 'Info collected for the host machine.', - anchor: 'host-processor', - }, - { - key: 'kubernetes', - title: 'Kubernetes', - description: 'Kubernetes metadata added by the kubernetes processor', - short_config: false, - anchor: 'kubernetes-processor', - fields: [ { - name: 'kubernetes', + 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: 'pod.name', + name: 'application', + level: 'extended', type: 'keyword', - description: 'Kubernetes pod name', + 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: 'pod.uid', + 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: 'Kubernetes Pod UID', + 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: 'namespace', + name: 'direction', + level: 'core', type: 'keyword', - description: 'Kubernetes namespace', + 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: 'node.name', + 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: 'Kubernetes node name', + 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: 'labels', + name: 'inner', + level: 'extended', type: 'object', - description: 'Kubernetes labels map', + 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: 'annotations', - type: 'object', - description: 'Kubernetes annotations map', + 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: 'container.name', + name: 'inner.vlan.name', + level: 'extended', type: 'keyword', - description: 'Kubernetes container name', + ignore_above: 1024, + description: 'Optional VLAN name as reported by the observer.', + example: 'outside', + default_field: false, }, { - name: 'container.image', + 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', - description: 'Kubernetes container image', + ignore_above: 1024, + description: 'Optional VLAN name as reported by the observer.', + example: 'outside', + default_field: false, }, ], }, - ], - }, - { - key: 'process', - title: 'Process', - description: 'Process metadata fields', - fields: [ { - name: 'process', + 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: 'exe', - type: 'alias', - path: 'process.executable', - migration: true, + 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, }, - ], - }, - ], - }, - { - key: 'log', - title: 'Log file content', - description: 'Contains log file lines.', - 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`.', - }, - { - name: 'log.source.address', - type: 'keyword', - required: false, - description: 'Source address from which the log event was read / sent from.', - }, - { - name: 'log.offset', - type: 'long', - required: false, - description: 'The file offset the reported line starts at.', - }, - { - name: 'stream', - type: 'keyword', - required: false, - description: "Log stream when reading container logs, can be 'stdout' or 'stderr' ", - }, - { - 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.', - }, - { - name: 'event.sequence', - type: 'long', - required: false, - description: 'The sequence number of this event.', - }, - { - name: 'syslog.facility', - type: 'long', - required: false, - description: 'The facility extracted from the priority.', - }, - { - name: 'syslog.priority', - type: 'long', - required: false, - description: 'The priority of the syslog event.', - }, - { - name: 'syslog.severity_label', - type: 'keyword', - required: false, - description: 'The human readable severity.', - }, - { - name: 'syslog.facility_label', - type: 'keyword', - required: false, - description: 'The human readable facility.', - }, - { - name: 'process.program', - type: 'keyword', - required: false, - description: 'The name of the program.', - }, - { - name: 'log.flags', - description: 'This field contains the flags of the event.', - }, - { - 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: '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: 'full_name', - type: 'keyword', + 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: 'fileset.name', - type: 'keyword', - description: 'The Filebeat fileset that generated this event.', - }, - { - name: 'fileset.module', - type: 'alias', - path: 'event.module', - migration: true, - }, - { - name: 'read_timestamp', - type: 'alias', - path: 'event.created', - migration: true, - }, - ], - }, - { - key: 'apache', - title: 'Apache', - description: 'Apache Module', - short_config: true, - fields: [ - { - name: 'apache2', + 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', - description: 'Aliases for backward compatibility with old apache2 fields', fields: [ { - name: 'access', - 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: '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: 'text', + type: 'text', + norms: false, + default_field: false, }, ], + description: 'Organization name.', }, ], }, { - name: 'apache', + name: 'os', + title: 'Operating System', + group: 2, + description: 'The OS fields contain information about the operating system.', type: 'group', - description: 'Apache fields.', fields: [ { - name: 'access', - type: 'group', - description: 'Contains fields for the Apache HTTP Server access logs.', - fields: [ - { - name: 'ssl.protocol', - type: 'keyword', - description: 'SSL protocol version.', - }, + 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: 'ssl.cipher', - type: 'keyword', - description: 'SSL cipher name.', + name: 'text', + type: 'text', + norms: false, + default_field: false, }, ], + description: 'Operating system name, including the version or code name.', + example: 'Mac OS Mojave', }, { - name: 'error', - type: 'group', - description: 'Fields from the Apache error logs.', - fields: [ + 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: 'module', - type: 'keyword', - description: 'The module producing the logged message.', + 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', }, ], }, - ], - }, - { - key: 'auditd', - title: 'Auditd', - description: 'Module for parsing auditd logs.', - short_config: true, - fields: [ { - name: 'user', + 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: 'terminal', + 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: - 'Terminal or tty device on which the user is performing the observed activity.', + '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: 'audit', - type: 'group', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'One or multiple unique identifiers of the user.', - }, - { - name: 'name', - type: 'keyword', - example: 'albert', - description: 'Short name or login of the user.', - }, - { - name: 'group.id', - type: 'keyword', - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'group.name', - type: 'keyword', - description: 'Name of the group.', - }, - ], + name: 'checksum', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Checksum of the installed package for verification.', + example: '68b329da9893e34099c7d8ad5cb9c940', }, { - name: 'effective', - type: 'group', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'One or multiple unique identifiers of the user.', - }, - { - name: 'name', - type: 'keyword', - example: 'albert', - description: 'Short name or login of the user.', - }, - { - name: 'group.id', - type: 'keyword', - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'group.name', - type: 'keyword', - description: 'Name of the group.', - }, - ], + 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: 'filesystem', - type: 'group', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'One or multiple unique identifiers of the user.', - }, - { - name: 'name', - type: 'keyword', - example: 'albert', - description: 'Short name or login of the user.', - }, - { - name: 'group.id', - type: 'keyword', - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'group.name', - type: 'keyword', - description: 'Name of the group.', - }, - ], + 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: 'owner', - type: 'group', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'One or multiple unique identifiers of the user.', - }, - { - name: 'name', - type: 'keyword', - example: 'albert', - description: 'Short name or login of the user.', - }, - { - name: 'group.id', - type: 'keyword', - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'group.name', - type: 'keyword', - description: 'Name of the group.', - }, - ], + name: 'installed', + level: 'extended', + type: 'date', + description: 'Time when package was installed.', }, { - name: 'saved', - type: 'group', - fields: [ - { - name: 'id', - type: 'keyword', - description: 'One or multiple unique identifiers of the user.', - }, - { - name: 'name', - type: 'keyword', - example: 'albert', - description: 'Short name or login of the user.', - }, - { - name: 'group.id', - type: 'keyword', - description: 'Unique identifier for the group on the system/platform.', - }, - { - name: 'group.name', - type: 'keyword', - description: 'Name of the group.', - }, - ], + 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: 'auditd', + name: 'pe', + title: 'PE Header', + group: 2, + description: 'These fields contain Windows Portable Executable (PE) metadata.', type: 'group', - description: 'Fields from the auditd logs.', fields: [ { - name: 'log', - type: 'group', + 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: - 'Fields from the Linux audit log. Not all fields are documented here because they are dynamic and vary by audit event type.', - fields: [ - { - name: 'old_auid', - description: - 'For login events this is the old audit ID used for the user prior to this login.', - }, - { - 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).', - }, - { - name: 'old_ses', - description: - 'For login events this is the old session ID used for the user prior to this login.', - }, - { - 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.', - }, - { - name: 'sequence', - type: 'long', - description: 'The audit event sequence number.', - }, - { - name: 'items', - description: 'The number of items in an event.', - }, + '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: '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.', + 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: 'tty', - type: 'keyword', - definition: 'TTY udevice the user is running programs on.', - }, - { - name: 'a0', - description: 'The first argument to the system call.', - }, - { - name: 'addr', - type: 'ip', - definition: 'Remote address that the user is connecting from.', - }, - { - name: 'rport', - type: 'long', - definition: 'Remote port number.', - }, - { - name: 'laddr', - type: 'ip', - definition: 'Local network address.', - }, - { - name: 'lport', - type: 'long', - definition: 'Local port number.', - }, - { - 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, + name: 'text', + type: 'text', + norms: false, + default_field: false, }, ], + description: 'Absolute path to the process executable.', + example: '/usr/bin/ssh', }, - ], - }, - ], - }, - { - key: 'elasticsearch', - title: 'elasticsearch', - description: 'elasticsearch Module', - fields: [ - { - name: 'elasticsearch', - type: 'group', - description: '', - fields: [ { - name: 'component', - description: 'Elasticsearch component from where the log event originated', - example: 'o.e.c.m.MetaDataCreateIndexService', - type: 'keyword', + 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: 'cluster.uuid', - description: 'UUID of the cluster', - example: 'GmvrbHlNTiSVYiPf8kxg9g', + name: 'hash.md5', + level: 'extended', type: 'keyword', + ignore_above: 1024, + description: 'MD5 hash.', }, { - name: 'cluster.name', - description: 'Name of the cluster', - example: 'docker-cluster', + name: 'hash.sha1', + level: 'extended', type: 'keyword', + ignore_above: 1024, + description: 'SHA1 hash.', }, { - name: 'node.id', - description: 'ID of the node', - example: 'DSiWcTyeThWtUXLB9J0BMw', + name: 'hash.sha256', + level: 'extended', type: 'keyword', + ignore_above: 1024, + description: 'SHA256 hash.', }, { - name: 'node.name', - description: 'Name of the node', - example: 'vWNJsZ3', + name: 'hash.sha512', + level: 'extended', type: 'keyword', + ignore_above: 1024, + description: 'SHA512 hash.', }, { - name: 'index.name', - description: 'Index name', - example: 'filebeat-test-input', + 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: 'index.id', - description: 'Index id', - example: 'aOGgDwbURfCV57AScqbCgw', + 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: 'shard.id', - description: 'Id of the shard', - example: '0', + 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: 'audit', - type: 'group', - description: '', - fields: [ - { - name: 'layer', - description: - 'The layer from which this event originated: rest, transport or ip_filter', - example: 'rest', - 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_admin', '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: 'event_type', - type: 'alias', - path: 'event.type', - migration: true, - }, + 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: 'origin_address', - type: 'alias', - path: 'source.ip', - migration: true, + 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: 'uri', - type: 'alias', - path: 'url.original', - migration: true, + 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: 'principal', - type: 'alias', - path: 'user.name', - migration: true, + name: 'text', + type: 'text', + norms: false, }, ], + description: 'Process name.\n\nSometimes called program name or similar.', + example: 'ssh', + default_field: false, }, { - name: 'deprecation', - type: 'group', - description: '', + name: 'parent.pgid', + level: 'extended', + type: 'long', + format: 'string', + description: 'Identifier of the group of processes the process belongs to.', + default_field: false, }, { - name: 'gc', - type: 'group', - description: 'GC fileset fields.', - fields: [ + 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: 'phase', - type: 'group', - description: 'Fields specific to GC phase.', - fields: [ - { - name: 'name', - type: 'keyword', - description: 'Name of the GC collection phase.', - }, - { - name: 'duration_sec', - type: 'float', - description: 'Collection phase duration according to the Java virtual machine.', - }, - { - name: 'scrub_symbol_table_time_sec', - type: 'float', - description: 'Pause time in seconds cleaning up symbol tables.', - }, - { - name: 'scrub_string_table_time_sec', - type: 'float', - description: 'Pause time in seconds cleaning up string tables.', - }, - { - name: 'weak_refs_processing_time_sec', - type: 'float', - description: 'Time spent processing weak references in seconds.', - }, - { - name: 'parallel_rescan_time_sec', - type: 'float', - description: - 'Time spent in seconds marking live objects while application is stopped.', - }, - { - name: 'class_unload_time_sec', - type: 'float', - description: 'Time spent unloading unused classes in seconds.', - }, - { - name: 'cpu_time', - type: 'group', - description: 'Process CPU time spent performing collections.', - fields: [ - { - name: 'user_sec', - type: 'float', - description: 'CPU time spent outside the kernel.', - }, - { - name: 'sys_sec', - type: 'float', - description: 'CPU time spent inside the kernel.', - }, - { - name: 'real_sec', - type: 'float', - description: - 'Total elapsed CPU time spent to complete the collection from start to finish.', - }, - ], - }, - ], + 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: 'jvm_runtime_sec', - type: 'float', - description: 'The time from JVM start up in seconds, as a floating point number.', - }, - { - name: 'threads_total_stop_time_sec', - type: 'float', - description: 'Garbage collection threads total stop time seconds.', - }, - { - name: 'stopping_threads_time_sec', - type: 'float', - description: 'Time took to stop threads seconds.', - }, - { - name: 'tags', - type: 'keyword', - description: 'GC logging tags.', - }, - { - name: 'heap', - type: 'group', - description: 'Heap allocation and total size.', - fields: [ - { - name: 'size_kb', - type: 'integer', - description: 'Total heap size in kilobytes.', - }, - { - name: 'used_kb', - type: 'integer', - description: 'Used heap in kilobytes.', - }, - ], - }, - { - name: 'old_gen', - type: 'group', - description: 'Old generation occupancy and total size.', - fields: [ - { - name: 'size_kb', - type: 'integer', - description: 'Total size of old generation in kilobytes.', - }, - { - name: 'used_kb', - type: 'integer', - description: 'Old generation occupancy in kilobytes.', - }, - ], - }, - { - name: 'young_gen', - type: 'group', - description: 'Young generation occupancy and total size.', - fields: [ - { - name: 'size_kb', - type: 'integer', - description: 'Total size of young generation in kilobytes.', - }, - { - name: 'used_kb', - type: 'integer', - description: 'Young generation occupancy in kilobytes.', - }, - ], + name: 'text', + type: 'text', + norms: false, }, ], + description: 'The working directory of the process.', + example: '/home/alice', + default_field: false, }, { - 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: '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: '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: 'pe.description', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Internal description of the file, provided at compile-time.', + example: 'Paint', + default_field: false, }, - ], - }, - ], - }, - { - key: 'haproxy', - title: 'haproxy', - description: 'haproxy Module', - fields: [ - { - name: 'haproxy', - type: 'group', - description: '', - fields: [ { - name: 'frontend_name', - description: - 'Name of the frontend (or listener) which received and processed the connection.', + 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: 'backend_name', - description: - 'Name of the backend (or listener) which was selected to manage the connection to the server.', + 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: 'server_name', - description: 'Name of the last server to which the connection was sent.', + 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: 'total_waiting_time_ms', - description: 'Total time in milliseconds spent waiting in the various queues', + name: 'pgid', + level: 'extended', type: 'long', + format: 'string', + description: 'Identifier of the group of processes the process belongs to.', }, { - name: 'connection_wait_time_ms', - description: - 'Total time in milliseconds spent waiting for the connection to establish to the final server', + name: 'pid', + level: 'core', type: 'long', + format: 'string', + description: 'Process id.', + example: 4242, }, { - name: 'bytes_read', - description: 'Total number of bytes transmitted to the client when the log is emitted.', + name: 'ppid', + level: 'extended', type: 'long', + format: 'string', + description: "Parent process' pid.", + example: 4241, }, { - name: 'time_queue', - description: 'Total time in milliseconds spent waiting in the various queues.', - type: 'long', + name: 'start', + level: 'extended', + type: 'date', + description: 'The time the process started.', + example: '2016-05-23T08:05:34.853Z', }, { - name: 'time_backend_connect', - description: - 'Total time in milliseconds spent waiting for the connection to establish to the final server, including retries.', + name: 'thread.id', + level: 'extended', type: 'long', + format: 'string', + description: 'Thread ID.', + example: 4242, }, { - name: 'server_queue', - description: - 'Total number of requests which were processed before this one in the server queue.', - type: 'long', + name: 'thread.name', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Thread name.', + example: 'thread-0', }, { - 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', + name: 'title', + level: 'extended', type: 'keyword', - description: 'The HAProxy source of the log', + 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: 'termination_state', - description: 'Condition the session was in when the session ended.', + name: 'uptime', + level: 'extended', + type: 'long', + description: 'Seconds the process has been up.', + example: 1325, }, { - name: 'mode', + name: 'working_directory', + level: 'extended', 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', - }, + ignore_above: 1024, + multi_fields: [ { - name: 'retries', - description: - 'Number of connection retries experienced by this session when trying to connect to the server.', - type: 'long', + 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: '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: '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: 'process_name', - type: 'alias', - path: 'process.name', - migration: true, + 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: 'pid', - type: 'alias', - path: 'process.pid', - migration: true, + name: 'data.type', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Standard registry type for encoding contents', + example: 'REG_SZ', + default_field: false, }, { - 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: 'hive', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Abbreviated name for the hive.', + example: 'HKLM', + default_field: false, }, { - 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.', - 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: '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: '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.', - }, - { - name: 'captured_headers', - description: - 'List of headers captured in the response due to the presence of the "capture response header" statement in the frontend.', - 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.', - }, - { - name: 'captured_headers', - description: - 'List of headers captured in the request due to the presence of the "capture request header" statement in the frontend.', - 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: '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: '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', - }, - ], + name: 'value', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Name of the value written.', + example: 'Debugger', + default_field: false, }, ], }, - ], - }, - { - key: 'icinga', - title: 'Icinga', - description: 'Icinga Module', - fields: [ { - name: 'icinga', + 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', - description: '', fields: [ { - name: 'debug', - type: 'group', - description: 'Contains fields for the Icinga debug logs.', - fields: [ - { - name: 'facility', - type: 'keyword', - description: 'Specifies what component of Icinga logged the message.', - }, - { - name: 'severity', - type: 'alias', - path: 'log.level', - migration: true, - }, - { - name: 'message', - type: 'alias', - path: 'message', - migration: true, - }, - ], + 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: 'main', - type: 'group', - description: 'Contains fields for the Icinga main logs.', - fields: [ - { - name: 'facility', - type: 'keyword', - description: 'Specifies what component of Icinga logged the message.', - }, - { - name: 'severity', - type: 'alias', - path: 'log.level', - migration: true, - }, - { - name: 'message', - type: 'alias', - path: 'message', - migration: true, - }, - ], + name: 'ip', + level: 'extended', + type: 'ip', + description: 'All of the IPs seen on your event.', }, { - name: 'startup', - type: 'group', - description: 'Contains fields for the Icinga startup logs.', - fields: [ - { - name: 'facility', - type: 'keyword', - description: 'Specifies what component of Icinga logged the message.', - }, - { - name: 'severity', - type: 'alias', - path: 'log.level', - migration: true, - }, - { - name: 'message', - type: 'alias', - path: 'message', - migration: true, - }, - ], + name: 'user', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'All the user names seen on your event.', + default_field: false, }, ], }, - ], - }, - { - key: 'iis', - title: 'IIS', - description: 'Module for parsing IIS log files.', - fields: [ { - name: 'iis', + 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', - description: 'Fields from IIS log files.', fields: [ { - name: 'access', - type: 'group', - description: 'Contains fields for IIS access logs.', - fields: [ - { - name: 'sub_status', - type: 'long', - description: 'The HTTP substatus code.', - }, - { - name: 'win32_status', - type: 'long', - description: 'The Windows status code.', - }, - { - name: 'site_name', - type: 'keyword', - description: 'The site name and instance number.', - }, - { - name: 'server_name', - type: 'keyword', - description: 'The name of the server on which the log file entry was generated.', - }, - { - name: 'cookie', - type: 'keyword', - description: 'The content of the cookie sent or received, if any.', - }, - { - 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: '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: '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: 'text', + type: 'text', + norms: false, + default_field: false, }, ], + description: 'Organization name.', + example: 'Google LLC', }, { - name: 'error', - type: 'group', - description: 'Contains fields for IIS error logs.', - fields: [ - { - name: 'reason_phrase', - type: 'keyword', - description: 'The HTTP reason phrase.', - }, - { - name: 'queue_name', - type: 'keyword', - description: 'The IIS application pool name.', - }, - { - 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, - }, - ], - }, - ], + name: 'bytes', + level: 'core', + type: 'long', + format: 'bytes', + description: 'Bytes sent from the server to the client.', + example: 184, }, - ], - }, - ], - }, - { - key: 'kafka', - title: 'Kafka', - description: 'Kafka module', - fields: [ - { - name: 'kafka', - type: 'group', - description: '', - fields: [ { - name: 'log', - type: 'group', - description: 'Kafka log lines.', - 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.', - }, - { - name: 'class', - type: 'keyword', - description: 'Java class the log is coming from.', - }, - { - name: 'trace', - type: 'group', - description: 'Trace in the log line.', - fields: [ - { - name: 'class', - type: 'keyword', - description: 'Java class the trace is coming from.', - }, - { - name: 'message', - type: 'text', - description: 'Message part of the trace.', - }, - ], - }, - ], + name: 'domain', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Server domain.', }, - ], - }, - ], - }, - { - key: 'kibana', - title: 'kibana', - description: 'kibana Module', - fields: [ - { - name: 'kibana', - type: 'group', - description: '', - fields: [ { - name: 'log', - type: 'group', - description: 'Kafka log lines.', - fields: [ - { - name: 'tags', - type: 'keyword', - description: 'Kibana logging tags.', - }, - { - name: 'state', - type: 'keyword', - description: 'Current state of Kibana.', - }, - { - 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: '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: 'kibana.log.meta.statusCode', - type: 'alias', - path: 'http.response.status_code', - migration: true, + 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: 'kibana.log.meta.method', - type: 'alias', - path: 'http.request.method', - migration: true, + name: 'text', + type: 'text', + norms: false, + default_field: false, }, ], + description: 'Short name or login of the user.', + example: 'albert', }, ], }, - ], - }, - { - key: 'logstash', - title: 'logstash', - description: 'logstash Module', - fields: [ { - name: 'logstash', + 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', - description: '', fields: [ { - name: 'log', - title: 'Logstash', - type: 'group', - description: 'Fields from the Logstash logs.', - fields: [ - { - name: 'module', - type: 'keyword', - description: 'The module or class where the event originate.', - }, - { - name: 'thread', - type: 'keyword', - description: 'Information about the running thread where the log originate.', - multi_fields: [ - { - name: 'text', - type: 'text', - }, - ], - }, - { - name: 'log_event', - type: 'object', - description: 'key and value debugging information.', - }, - { - name: 'message', - type: 'alias', - path: 'message', - migration: true, - }, - { - name: 'level', - type: 'alias', - path: 'log.level', - migration: true, - }, - ], + 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: 'slowlog', - type: 'group', - description: 'slowlog', - fields: [ - { - name: 'module', - type: 'keyword', - description: 'The module or class where the event originate.', - }, - { - name: 'thread', - type: 'keyword', - description: 'Information about the running thread where the log originate.', - multi_fields: [ - { - name: 'text', - type: 'text', - }, - ], - }, - { - name: 'event', - type: 'keyword', - description: 'Raw dump of the original event', - multi_fields: [ - { - name: 'text', - type: 'text', - }, - ], - }, - { - name: 'plugin_name', - type: 'keyword', - description: 'Name of the plugin', - }, - { - name: 'plugin_type', - type: 'keyword', - description: 'Type of the plugin: Inputs, Filters, Outputs or Codecs.', - }, - { - name: 'took_in_millis', - type: 'long', - description: 'Execution time for the plugin in milliseconds.', - }, - { - name: 'plugin_params', - type: 'keyword', - description: 'String value of the plugin configuration', - multi_fields: [ - { - name: 'text', - type: 'text', - }, - ], - }, - { - name: 'plugin_params_object', - type: 'object', - description: 'key -> value of the configuration used by the plugin.', - }, - { - name: 'level', - type: 'alias', - path: 'log.level', - migration: true, - }, - { - name: 'took_in_nanos', - type: 'alias', - path: 'event.duration', - migration: true, - }, - ], + 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', }, ], }, - ], - }, - { - key: 'mongodb', - title: 'mongodb', - description: 'Module for parsing MongoDB log files.', - fields: [ { - name: 'mongodb', + 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', - description: 'Fields from MongoDB logs.', fields: [ { - name: 'log', - type: 'group', - description: 'Contains fields from MongoDB logs.', - fields: [ - { - name: 'component', - description: 'Functional categorization of message', - example: 'COMMAND', - type: 'keyword', - }, - { - name: 'context', - description: 'Context of message', - example: 'initandlisten', - type: 'keyword', - }, - { - name: 'severity', - type: 'alias', - path: 'log.level', - migration: true, - }, + 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: 'message', - type: 'alias', - path: 'message', - migration: true, + name: 'text', + type: 'text', + norms: false, + default_field: false, }, ], + description: 'Organization name.', + example: 'Google LLC', }, - ], - }, - ], - }, - { - key: 'mysql', - title: 'MySQL', - description: 'Module for parsing the MySQL log files.', - short_config: true, - fields: [ - { - name: 'mysql', - type: 'group', - description: 'Fields from the MySQL log files.', - fields: [ { - name: 'thread_id', + name: 'bytes', + level: 'core', type: 'long', - description: 'The connection or thread ID for the query.', + format: 'bytes', + description: 'Bytes sent from the source to the destination.', + example: 184, }, { - name: 'error', - type: 'group', - description: 'Contains fields from the MySQL error logs.', - fields: [ - { - name: 'thread_id', - type: 'alias', - path: 'mysql.thread_id', - migration: true, - }, - { - name: 'level', - type: 'alias', - path: 'log.level', - migration: true, - }, + 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: 'message', - type: 'alias', - path: 'message', - migration: true, + name: 'text', + type: 'text', + norms: false, + default_field: false, }, ], + description: "User's full name, if available.", + example: 'Albert Einstein', }, { - name: 'slowlog', - type: 'group', - description: 'Contains fields from the MySQL slow logs.', - 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.', - }, - { - name: 'rows_sent', - type: 'long', - description: 'The number of rows returned by the query.', - }, - { - name: 'rows_examined', - type: 'long', - description: 'The number of rows scanned by the query.', - }, - { - name: 'rows_affected', - type: 'long', - description: 'The number of rows modified by the query.', - }, - { - name: 'bytes_sent', - type: 'long', - format: 'bytes', - description: 'The size of the query result.', - }, - { - name: 'query', - description: 'The slow query.', - }, - { - name: 'id', - type: 'alias', - path: 'mysql.thread_id', - migration: true, - }, - { - name: 'schema', - type: 'keyword', - description: 'The schema where the slow query was executed.', - }, - { - name: 'current_user', - type: 'keyword', - description: - 'Current authenticated user, used to determine access privileges. Can differ from the value for user.', - }, - { - name: 'last_errno', - type: 'keyword', - description: 'Last SQL error seen.', - }, - { - name: 'killed', - type: 'keyword', - description: 'Code of the reason if the query was killed.', - }, - { - name: 'query_cache_hit', - type: 'boolean', - description: 'Whether the query cache was hit.', - }, - { - name: 'tmp_table', - type: 'boolean', - description: 'Whether a temporary table was used to resolve the query.', - }, - { - name: 'tmp_table_on_disk', - type: 'boolean', - description: 'Whether the query needed temporary tables on disk.', - }, - { - name: 'tmp_tables', - type: 'long', - description: 'Number of temporary tables created for this query', - }, - { - name: 'tmp_disk_tables', - type: 'long', - description: 'Number of temporary tables created on disk for this query.', - }, - { - 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.', - }, - { - name: 'filesort_on_disk', - type: 'boolean', - description: - 'Whether filesort optimization was used and it needed temporary tables on disk.', - }, - { - name: 'priority_queue', - type: 'boolean', - description: 'Whether a priority queue was used for filesort.', - }, - { - name: 'full_scan', - type: 'boolean', - description: 'Whether a full table scan was needed for the slow query.', - }, - { - name: 'full_join', - type: 'boolean', - description: - 'Whether a full join was needed for the slow query (no indexes were used for joins).', - }, - { - name: 'merge_passes', - type: 'long', - description: 'Number of merge passes executed for the query.', - }, - { - 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.', - }, - { - 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.', - }, - { - name: 'innodb', - type: 'group', - description: 'Contains fields relative to InnoDB engine', - fields: [ - { - name: 'trx_id', - type: 'keyword', - description: 'Transaction ID', - }, - { - name: 'io_r_ops', - type: 'long', - description: 'Number of page read operations.', - }, - { - name: 'io_r_bytes', - type: 'long', - format: 'bytes', - description: 'Bytes read during page read operations.', - }, - { - name: 'io_r_wait.sec', - type: 'long', - description: 'How long it took to read all needed data from storage.', - }, - { - name: 'rec_lock_wait.sec', - type: 'long', - description: 'How long the query waited for locks.', - }, - { - 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.', - }, - { - name: 'pages_distinct', - type: 'long', - description: 'Approximated count of pages accessed to execute the query.', - }, - ], - }, - { - name: 'user', - type: 'alias', - path: 'user.name', - migration: true, - }, - { - name: 'host', - type: 'alias', - path: 'source.domain', - migration: true, - }, + 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: 'ip', - type: 'alias', - path: 'source.ip', - migration: true, + name: 'text', + type: 'text', + norms: false, + default_field: false, }, ], + description: 'Short name or login of the user.', + example: 'albert', }, ], }, - ], - }, - { - key: 'nats', - title: 'nats', - description: 'Module for parsing NATS log files.', - release: 'beta', - fields: [ { - name: 'nats', + 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', - description: 'Fields from NATS logs.', fields: [ { - name: 'log', - type: 'group', - description: 'Nats log files', - release: 'beta', + 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/', }, ], }, - ], - }, - { - key: 'nginx', - title: 'Nginx', - description: 'Module for parsing the Nginx log files.', - short_config: true, - fields: [ { - name: 'nginx', + 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', - description: 'Fields from the Nginx log files.', fields: [ { - name: 'access', - type: 'group', - description: 'Contains fields for the Nginx access logs.', - 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`.', - }, - { - name: 'body_sent.bytes', - type: 'alias', - path: 'http.response.body.bytes', - migration: true, - }, - { - name: 'remote_ip', - type: 'alias', - path: 'source.ip', - 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: '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: 'error', - type: 'group', - description: 'Contains fields for the Nginx error logs.', - fields: [ - { - name: 'connection_id', - type: 'long', - description: 'Connection identifier.', - }, - { - 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: '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, }, - ], - }, - ], - }, - { - key: 'osquery', - title: 'Osquery', - description: 'Fields exported by the `osquery` module', - fields: [ - { - name: 'osquery', - type: 'group', - description: '', - fields: [ { - name: 'result', - type: 'group', - description: 'Common fields exported by the result metricset.', - fields: [ - { - name: 'name', - type: 'keyword', - description: 'The name of the query that generated this event.', - }, - { - 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".', - }, - { - name: 'host_identifier', - type: 'keyword', - description: - 'The identifier for the host on which the osquery agent is running. Normally the hostname.', - }, - { - name: 'unix_time', - type: 'long', - description: - 'Unix timestamp of the event, in seconds since the epoch. Used for computing the `@timestamp` column.', - }, - { - name: 'calendar_time', - type: 'keyword', - description: - 'String representation of the collection time, as formatted by osquery.', - }, - ], + 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, }, - ], - }, - ], - }, - { - key: 'postgresql', - title: 'PostgreSQL', - description: 'Module for parsing the PostgreSQL log files.', - short_config: true, - fields: [ - { - name: 'postgresql', - type: 'group', - description: 'Fields from PostgreSQL logs.', - fields: [ { - name: 'log', - type: 'group', - description: 'Fields from the PostgreSQL log files.', - fields: [ - { - name: 'timestamp', - description: 'The timestamp from the log line.', - }, - { - name: 'core_id', - type: 'long', - description: 'Core id', - }, - { - name: 'database', - example: 'mydb', - description: 'Name of database', - }, - { - name: 'query', - example: 'SELECT * FROM users;', - description: 'Query statement.', - }, - { - 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, - }, + 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, }, ], }, - ], - }, - { - key: 'redis', - title: 'Redis', - description: 'Redis Module', - fields: [ { - name: 'redis', + 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', - description: '', fields: [ { - name: 'log', - type: 'group', - description: 'Redis log files', - 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`.', - }, - { - 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: '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: 'slowlog', - type: 'group', - description: 'Slow logs are retrieved from Redis via a network connection.', - fields: [ - { - name: 'cmd', - type: 'keyword', - description: 'The command executed.', - }, - { - name: 'duration.us', - type: 'long', - description: 'How long it took to execute the command in microseconds.', - }, - { - name: 'id', - type: 'long', - description: 'The ID of the query.', - }, - { - name: 'key', - type: 'keyword', - description: 'The key on which the command was executed.', - }, - { - name: 'args', - type: 'keyword', - description: 'The arguments with which the command was called.', - }, - ], + 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', }, ], }, - ], - }, - { - key: 'santa', - title: 'Google Santa', - description: 'Santa Module', - fields: [ { - name: 'santa', + 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', - description: '', fields: [ { - name: 'action', + name: 'domain', + level: 'extended', type: 'keyword', - example: 'EXEC', - description: 'Action', + 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: 'decision', + name: 'extension', + level: 'extended', type: 'keyword', - example: 'ALLOW', - description: 'Decision that santad took.', + 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: 'reason', + name: 'fragment', + level: 'extended', type: 'keyword', - example: 'CERT', - description: 'Reason for the decsision.', + ignore_above: 1024, + description: + 'Portion of the url after the `#`, such as "top".\n\nThe `#` is not part of the fragment.', }, { - name: 'mode', + name: 'full', + level: 'extended', type: 'keyword', - example: 'M', - description: 'Operating mode of Santa.', - }, - { - name: 'disk', - type: 'group', - description: 'Fields for DISKAPPEAR actions.', - fields: [ + ignore_above: 1024, + multi_fields: [ { - name: 'volume', - description: 'The volume name.', + 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: 'bus', - description: 'The disk bus protocol.', + name: 'text', + type: 'text', + norms: false, + default_field: false, }, - { - name: 'serial', - description: 'The disk serial number.', + ], + 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: 'bsdname', - example: 'disk1s3', - description: 'The disk BSD name.', + 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: 'model', - example: 'APPLE SSD SM0512L', - description: 'The disk model.', + 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: 'fs', - example: 'apfs', - description: 'The disk volume kind (filesystem type).', + 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: 'mount', - description: 'The disk volume path.', + 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: 'certificate.common_name', - type: 'keyword', - description: 'Common name from code signing certificate.', - }, - { - name: 'certificate.sha256', - type: 'keyword', - description: 'SHA256 hash of code signing certificate.', - }, - { - name: 'hash.sha256', - type: 'keyword', - description: 'Hash of process executable.', + 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, + }, + ], }, - ], - }, - { - key: 'system', - title: 'System', - description: 'Module for parsing system log files.', - short_config: true, - fields: [ { - name: 'system', + name: 'vulnerability', + title: 'Vulnerability', + group: 2, + description: + 'The vulnerability fields describe information about a vulnerability\nthat is relevant to an event.', type: 'group', - description: 'Fields from the system log files.', fields: [ { - name: 'auth', - type: 'group', - description: 'Fields from the Linux authorization logs.', - 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: '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".', - }, - { - name: 'signature', - description: 'The signature of the client public key.', - }, - { - name: 'dropped_ip', - type: 'ip', - description: - 'The client IP from SSH connections that are open and immediately dropped.', - }, - { - name: 'event', - type: 'alias', - path: 'event.action', - migration: true, - }, - { - 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.', - fields: [ - { - name: 'error', - example: 'user NOT in sudoers', - description: 'The error message in case the sudo command failed.', - }, - { - name: 'tty', - description: 'The TTY where the sudo command is executed.', - }, - { - name: 'pwd', - description: 'The current directory where the sudo command is executed.', - }, - { - name: 'user', - example: 'root', - description: 'The target user to which the sudo command is switching.', - }, - { - name: 'command', - description: 'The command executed via sudo.', - }, - ], - }, - { - name: 'useradd', - type: 'group', - description: 'Fields specific to events created by the `useradd` command.', - 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.', - fields: [ - { - name: 'name', - type: 'alias', - path: 'group.name', - migration: true, - }, - { - name: 'gid', - type: 'alias', - path: 'group.id', - migration: true, - }, - ], + 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: 'syslog', - type: 'group', - description: 'Contains fields from the syslog system logs.', - 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: '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, }, - ], - }, - ], - }, - { - key: 'traefik', - title: 'Traefik', - description: 'Module for parsing the Traefik log files.', - fields: [ - { - name: 'traefik', - type: 'group', - description: 'Fields from the Traefik log files.', - fields: [ { - name: 'access', - type: 'group', - description: 'Contains fields for the Traefik access logs.', - fields: [ - { - name: 'user_identifier', - type: 'keyword', - description: 'Is the RFC 1413 identity of the client', - }, - { - name: 'request_count', - type: 'long', - description: 'The number of requests', - }, - { - name: 'frontend_name', - type: 'keyword', - description: 'The name of the frontend used', - }, - { - 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: 'iptables', - title: 'iptables', - description: 'Module for handling the iptables logs.', - fields: [ - { - name: 'iptables', - type: 'group', - description: 'Fields from the iptables logs.', - fields: [ - { - name: 'ether_type', - type: 'long', - description: 'Value of the ethernet type field identifying the network layer protocol.', - }, - { - name: 'flow_label', - type: 'integer', - description: 'IPv6 flow label.', - }, - { - name: 'fragment_flags', + name: 'id', + level: 'extended', type: 'keyword', - description: 'IP fragment flags. A combination of CE, DF and MF.', - }, - { - name: 'fragment_offset', - type: 'long', - description: 'Offset of the current IP fragment.', - }, - { - name: 'icmp', - type: 'group', - description: 'ICMP fields.', - fields: [ - { - name: 'code', - type: 'long', - description: 'ICMP code.', - }, - { - name: 'id', - type: 'long', - description: 'ICMP ID.', - }, - { - name: 'parameter', - type: 'long', - description: 'ICMP parameter.', - }, - { - name: 'redirect', - type: 'ip', - description: 'ICMP redirect address.', - }, - { - name: 'seq', - type: 'long', - description: 'ICMP sequence number.', - }, - { - name: 'type', - type: 'long', - description: 'ICMP type.', - }, - ], + 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: 'id', - type: 'long', - description: 'Packet identifier.', + 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: 'incomplete_bytes', - type: 'long', - description: 'Number of incomplete bytes.', + name: 'report_id', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'The report or scan identification number.', + example: 20191018.0001, + default_field: false, }, { - name: 'input_device', + name: 'scanner.vendor', + level: 'extended', type: 'keyword', - description: 'Device that received the packet.', + ignore_above: 1024, + description: 'The name of the vulnerability scanner vendor.', + example: 'Tenable', + default_field: false, }, { - name: 'precedence_bits', - type: 'short', - description: 'IP precedence bits.', + 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: 'tos', - type: 'long', - description: 'IP Type of Service field.', + 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: 'length', - type: 'long', - description: 'Packet length.', + 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: 'output_device', + name: 'score.version', + level: 'extended', type: 'keyword', - description: 'Device that output the packet.', - }, - { - name: 'tcp', - type: 'group', - description: 'TCP fields.', - fields: [ - { - name: 'flags', - type: 'keyword', - description: 'TCP flags.', - }, - { - name: 'reserved_bits', - type: 'short', - description: 'TCP reserved bits.', - }, - { - name: 'seq', - type: 'long', - description: 'TCP sequence number.', - }, - { - name: 'ack', - type: 'long', - description: 'TCP Acknowledgment number.', - }, - { - name: 'window', - type: 'long', - description: 'Advertised TCP window size.', - }, - ], - }, - { - name: 'ttl', - type: 'integer', - description: 'Time To Live field.', + 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: 'udp', - type: 'group', - description: 'UDP fields.', - fields: [ - { - name: 'length', - type: 'long', - description: 'Length of the UDP header and payload.', - }, - ], + 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, }, - { - name: 'ubiquiti', - type: 'group', - description: 'Fields for Ubiquiti network devices.', - fields: [ - { - name: 'input_zone', - type: 'keyword', - description: 'Input zone.', - }, - { - name: 'output_zone', - type: 'keyword', - description: 'Output zone.', - }, - { - name: 'rule_number', - type: 'keyword', - description: 'The rule number within the rule set.', - }, - { - name: 'rule_set', - type: 'keyword', - description: 'The rule set name.', - }, - ], + ], + }, + ], + }, + { + 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: '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.', + 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: 'suricata', - title: 'Suricata', - description: 'Module for handling the EVE JSON logs produced by Suricata.', + key: 'kubernetes', + title: 'Kubernetes', + description: 'Kubernetes metadata added by the kubernetes processor\n', + short_config: false, + anchor: 'kubernetes-processor', fields: [ { - name: 'suricata', + name: 'kubernetes', type: 'group', - description: 'Fields from the Suricata EVE log file.', fields: [ { - name: 'eve', - type: 'group', - description: 'Fields exported by the EVE JSON logs', - 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: '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: 'flow_id', + name: 'full_name', type: 'keyword', }, - { - name: 'email', - type: 'group', - fields: [ - { - name: 'status', - type: 'keyword', - }, - ], - }, - { - name: 'dest_ip', - type: 'alias', - path: 'destination.ip', + ], + }, + ], + }, + { + 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: 'icmp_code', - type: 'long', + name: 'ssl.protocol', + type: 'alias', + path: 'apache.access.ssl.protocol', + migration: true, }, { - name: 'http', + 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: 'status', + name: 'device', type: 'alias', - path: 'http.response.status_code', + path: 'user_agent.device.name', + migration: true, }, { - name: 'redirect', - type: 'keyword', + name: 'name', + type: 'alias', + path: 'user_agent.name', + migration: true, }, { - name: 'http_user_agent', + name: 'os', type: 'alias', - path: 'user_agent.original', + path: 'user_agent.os.full_name', + migration: true, }, { - name: 'protocol', - type: 'keyword', + name: 'os_name', + type: 'alias', + path: 'user_agent.os.name', + migration: true, }, { - name: 'http_refer', + name: 'original', type: 'alias', - path: 'http.request.referrer', + path: 'user_agent.original', + migration: true, + }, + ], + }, + { + name: 'geoip', + type: 'group', + fields: [ + { + name: 'continent_name', + type: 'alias', + path: 'source.geo.continent_name', + migration: true, }, { - name: 'url', + name: 'country_iso_code', type: 'alias', - path: 'url.original', + path: 'source.geo.country_iso_code', + migration: true, }, { - name: 'hostname', + name: 'location', type: 'alias', - path: 'url.domain', + path: 'source.geo.location', + migration: true, }, { - name: 'length', + name: 'region_name', type: 'alias', - path: 'http.response.body.bytes', + path: 'source.geo.region_name', + migration: true, }, { - name: 'http_method', + name: 'city_name', type: 'alias', - path: 'http.request.method', + path: 'source.geo.city_name', + migration: true, }, { - name: 'http_content_type', - type: 'keyword', + name: 'region_iso_code', + type: 'alias', + path: 'source.geo.region_iso_code', + migration: true, }, ], }, + ], + }, + { + name: 'error', + type: 'group', + fields: [ { - name: 'timestamp', + name: 'level', type: 'alias', - path: '@timestamp', + path: 'log.level', + migration: true, }, { - name: 'in_iface', - type: 'keyword', + name: 'message', + type: 'alias', + path: 'message', + migration: true, }, { - 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: 'pid', + type: 'alias', + path: 'process.pid', + migration: true, }, { - 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: 'tid', + type: 'alias', + path: 'process.thread.id', + migration: true, }, { - 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', + 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: 'segment_memcap_drop', + name: 'avg_pkt_size', type: 'long', }, { - name: 'overlap', + name: 'bytes', type: 'long', }, { - name: 'insert_list_fail', + name: 'tcp', type: 'long', }, { - name: 'rst', + name: 'raw', type: 'long', }, { - name: 'stream_depth_reached', + name: 'ppp', type: 'long', }, { - name: 'reassembly_memuse', + name: 'vlan_qinq', type: 'long', }, { - name: 'reassembly_gap', + name: 'null', type: 'long', }, { - name: 'overlap_diff_data', + 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: 'no_flow', - 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: '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: '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: 'dns', - type: 'group', - fields: [ - { - name: 'memcap_global', - type: 'long', - }, - { - name: 'memcap_state', - type: 'long', - }, - { - name: 'memuse', - type: 'long', - }, - ], + 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: '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: 'passive', + type: 'boolean', + description: 'Whether PASV mode is toggled for control channel.\n', }, { - 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: '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: 'tls', + 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: 'notbefore', - type: 'date', + name: 'indicator', + type: 'keyword', + description: 'The intelligence indicator.\n', }, { - name: 'issuerdn', + name: 'indicator_type', type: 'keyword', + description: 'The type of data the indicator represents.\n', }, { - name: 'sni', + name: 'host', type: 'keyword', + description: + 'If the indicator type was Intel::ADDR, then this field will be present.\n', }, { - name: 'version', + 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: 'session_resumed', - type: 'boolean', + name: 'where', + type: 'keyword', + description: 'Where the data was discovered.\n', }, { - name: 'fingerprint', + name: 'node', type: 'keyword', + description: 'The name of the node where the match was discovered.\n', }, { - name: 'serial', + 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: 'notafter', - type: 'date', + 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: 'subject', + 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: 'app_proto_ts', + name: 'matched', type: 'keyword', + description: + 'Event to represent a match in the intelligence data from data that was seen.\n', }, { - name: 'flow', + 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: 'bytes_toclient', - type: 'alias', - path: 'destination.bytes', + 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: 'start', - type: 'alias', - path: 'event.start', + 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: 'pkts_toclient', - type: 'alias', - path: 'destination.packets', + name: 'code', + type: 'integer', + description: 'Error code.\n', }, { - name: 'age', - type: 'long', + name: 'msg', + type: 'keyword', + description: 'Error message.\n', }, + ], + }, + { + name: 'valid', + type: 'group', + fields: [ { - name: 'state', - type: 'keyword', + name: 'from', + type: 'date', + description: 'Ticket valid from.\n', }, { - name: 'bytes_toserver', - type: 'alias', - path: 'source.bytes', + name: 'until', + type: 'date', + description: 'Ticket valid until.\n', }, { - name: 'reason', + 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: 'pkts_toserver', - type: 'alias', - path: 'source.packets', + name: 'new', + type: 'keyword', + description: 'Hash of ticket returned by the KDC.\n', }, + ], + }, + { + name: 'cert', + type: 'group', + fields: [ { - name: 'end', - type: 'date', + 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: 'alerted', - type: 'boolean', + 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: 'app_proto', - type: 'alias', - path: 'network.protocol', + name: 'function', + type: 'keyword', + description: 'The name of the function message that was sent.\n', }, { - name: 'tx_id', - type: 'long', + 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: 'app_proto_tc', + name: 'arg', type: 'keyword', + description: 'The argument issued to the command.\n', }, { - name: 'smtp', - type: 'group', - fields: [ - { - name: 'rcpt_to', - type: 'keyword', - }, - { - name: 'mail_from', - type: 'keyword', - }, - { - name: 'helo', - type: 'keyword', - }, - ], + name: 'success', + type: 'boolean', + description: 'Whether the command succeeded.\n', }, { - name: 'app_proto_expected', - type: 'keyword', + name: 'rows', + type: 'integer', + description: 'The number of affected rows, if any.\n', }, { - name: 'flags', - type: 'group', + name: 'response', + type: 'keyword', + description: 'Server message, if any.\n', }, ], }, - ], - }, - ], - }, - { - key: 'zeek', - title: 'Zeek', - description: 'Module for handling logs produced by Zeek/Bro', - fields: [ - { - name: 'zeek', - type: 'group', - description: 'Fields from Zeek/Bro logs after normalization', - fields: [ - { - name: 'session_id', - type: 'keyword', - }, - { - name: 'connection.local_orig', - type: 'boolean', - }, - { - name: 'connection.local_resp', - type: 'boolean', - }, - { - name: 'connection.missed_bytes', - type: 'long', - }, - { - name: 'connection.state', - type: 'keyword', - }, - { - name: 'connection.history', - type: 'keyword', - }, - { - name: 'connection.orig_l2_addr', - type: 'keyword', - }, - { - name: 'resp_l2_addr', - type: 'keyword', - }, - { - name: 'vlan', - type: 'keyword', - }, - { - name: 'inner_vlan', - type: 'keyword', - }, - { - name: 'dns.trans_id', - type: 'integer', - }, - { - name: 'dns.rtt', - type: 'double', - }, - { - name: 'dns.query', - type: 'keyword', - }, - { - name: 'dns.qclass', - type: 'long', - }, - { - name: 'dns.qclass_name', - type: 'keyword', - }, - { - name: 'dns.qtype', - type: 'long', - }, - { - name: 'dns.qtype_name', - type: 'keyword', - }, - { - name: 'dns.rcode', - type: 'long', - }, - { - name: 'dns.rcode_name', - type: 'keyword', - }, - { - name: 'dns.AA', - type: 'boolean', - }, - { - name: 'dns.TC', - type: 'boolean', - }, - { - name: 'dns.RD', - type: 'boolean', - }, - { - name: 'dns.RA', - type: 'boolean', - }, - { - name: 'dns.answers', - type: 'keyword', - }, - { - name: 'dns.TTLs', - type: 'double', - }, - { - name: 'dns.rejected', - type: 'boolean', - }, - { - name: 'dns.total_answers', - type: 'integer', - }, - { - name: 'dns.total_replies', - type: 'integer', - }, - { - name: 'dns.saw_query', - type: 'boolean', - }, - { - name: 'dns.saw_reply', - type: 'boolean', - }, { - name: 'http.trans_depth', - type: 'integer', - }, - { - name: 'http.status_msg', - type: 'keyword', - }, - { - name: 'http.info_code', - type: 'integer', - }, - { - name: 'http.info_msg', - type: 'keyword', - }, - { - name: 'http.filename', - type: 'keyword', - }, - { - name: 'http.tags', - type: 'keyword', - }, - { - name: 'http.captured_password', - type: 'boolean', - }, - { - name: 'http.proxied', - type: 'keyword', - }, - { - name: 'http.range_request', - type: 'boolean', - }, - { - name: 'http.client_header_names', - type: 'keyword', - }, - { - name: 'http.server_header_names', - type: 'keyword', - }, - { - name: 'http.orig_fuids', - type: 'keyword', - }, - { - name: 'http.orig_mime_types', - type: 'keyword', - }, - { - name: 'http.orig_filenames', - type: 'keyword', - }, - { - name: 'http.resp_fuids', - type: 'keyword', - }, - { - name: 'http.resp_mime_types', - type: 'keyword', - }, - { - name: 'http.resp_filenames', - type: 'keyword', - }, - { - name: 'http.orig_mime_depth', - type: 'integer', - }, - { - name: 'http.resp_mime_depth', - type: 'integer', - }, - { - name: 'files.fuid', - type: 'keyword', - }, - { - name: 'files.tx_host', - type: 'ip', - }, - { - name: 'files.rx_host', - type: 'ip', - }, - { - name: 'files.session_ids', - type: 'keyword', - }, - { - name: 'files.source', - type: 'keyword', - }, - { - name: 'files.depth', - type: 'long', - }, - { - name: 'files.direction', - type: 'keyword', - }, - { - name: 'files.analyzers', - type: 'keyword', - }, - { - name: 'files.mime_type', - type: 'keyword', - }, - { - name: 'files.filename', - type: 'keyword', - }, - { - name: 'files.local_orig', - type: 'boolean', - }, - { - name: 'files.is_orig', - type: 'boolean', - }, - { - name: 'files.duration', - type: 'double', - }, - { - name: 'files.seen_bytes', - type: 'long', - }, - { - name: 'files.total_bytes', - type: 'long', - }, - { - name: 'files.missing_bytes', - type: 'long', - }, - { - name: 'files.overflow_bytes', - type: 'long', - }, - { - name: 'files.timedout', - type: 'boolean', - }, - { - name: 'files.parent_fuid', - type: 'keyword', - }, - { - name: 'files.md5', - type: 'keyword', - }, - { - name: 'files.sha1', - type: 'keyword', - }, - { - name: 'files.sha256', - type: 'keyword', - }, - { - name: 'files.extracted', - type: 'keyword', + 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: 'files.extracted_cutoff', - type: 'boolean', + 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: 'files.extracted_size', - type: 'long', + 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: 'files.entropy', - type: 'double', + 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: 'ssl.version', - type: 'keyword', + 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: 'ssl.cipher', - type: 'keyword', + 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: 'ssl.curve', - type: 'keyword', + 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: 'ssl.server_name', - type: 'keyword', + 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: 'ssl.resumed', - type: 'boolean', + 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: 'ssl.next_protocol', - type: 'keyword', + 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: 'ssl.established', - type: 'boolean', + 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: 'ssl.cert_chain', - type: 'keyword', + 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: 'ssl.cert_chain_fuids', - type: 'keyword', + 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: 'ssl.client_cert_chain', - type: 'keyword', + 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: 'ssl.client_cert_chain_fuids', - type: 'keyword', + 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.issuer', - type: 'keyword', + 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: 'ssl.client_issuer', - type: 'keyword', + 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: 'ssl.validation_status', - type: 'keyword', + 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: 'ssl.subject', - type: 'keyword', + 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: 'ssl.client_subject', - type: 'keyword', + 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: 'ssl.last_alert', - type: 'keyword', + 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', + }, + ], }, ], }, @@ -7293,47 +18360,47 @@ export const filebeatSchema: Schema = [ { key: 'netflow', title: 'NetFlow', - description: 'Fields from NetFlow and IPFIX flows.', + description: 'Fields from NetFlow and IPFIX flows.\n', fields: [ { name: 'netflow', type: 'group', - description: 'Fields from NetFlow and IPFIX.', + description: 'Fields from NetFlow and IPFIX.\n', fields: [ { name: 'type', type: 'keyword', - description: 'The type of NetFlow record described by this event.', + 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.', + 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. ", + description: "Exporter's network address in IP:port format.\n", }, { name: 'source_id', type: 'long', - description: 'Observation domain ID to which this record belongs.', + description: 'Observation domain ID to which this record belongs.\n', }, { name: 'timestamp', type: 'date', - description: 'Time and date of export.', + description: 'Time and date of export.\n', }, { name: 'uptime_millis', type: 'long', - description: 'How long the exporter process has been running, in milliseconds.', + description: 'How long the exporter process has been running, in milliseconds.\n', }, { name: 'version', - type: 'long', - description: 'NetFlow version used.', + type: 'integer', + description: 'NetFlow version used.\n', }, ], }, @@ -7539,7 +18606,7 @@ export const filebeatSchema: Schema = [ }, { name: 'class_id', - type: 'short', + type: 'long', }, { name: 'minimum_ttl', @@ -8118,19 +19185,19 @@ export const filebeatSchema: Schema = [ type: 'long', }, { - name: 'post_nast_ource_ipv4_address', + name: 'post_nat_source_ipv4_address', type: 'ip', }, { - name: 'post_nadt_estination_ipv4_address', + name: 'post_nat_destination_ipv4_address', type: 'ip', }, { - name: 'post_napst_ource_transport_port', + name: 'post_napt_source_transport_port', type: 'integer', }, { - name: 'post_napdt_estination_transport_port', + name: 'post_napt_destination_transport_port', type: 'integer', }, { @@ -8342,11 +19409,11 @@ export const filebeatSchema: Schema = [ type: 'long', }, { - name: 'post_nast_ource_ipv6_address', + name: 'post_nat_source_ipv6_address', type: 'ip', }, { - name: 'post_nadt_estination_ipv6_address', + name: 'post_nat_destination_ipv6_address', type: 'ip', }, { @@ -8514,11 +19581,11 @@ export const filebeatSchema: Schema = [ type: 'long', }, { - name: 'hash_ipp_ayload_offset', + name: 'hash_ip_payload_offset', type: 'long', }, { - name: 'hash_ipp_ayload_size', + name: 'hash_ip_payload_size', type: 'long', }, { @@ -8550,11 +19617,11 @@ export const filebeatSchema: Schema = [ type: 'keyword', }, { - name: 'upper_cli_imit', + name: 'upper_ci_limit', type: 'double', }, { - name: 'lower_cli_imit', + name: 'lower_ci_limit', type: 'double', }, { @@ -8718,11 +19785,11 @@ export const filebeatSchema: Schema = [ type: 'long', }, { - name: 'distinct_count_of_sourc_eipa_ddress', + name: 'distinct_count_of_source_ip_address', type: 'long', }, { - name: 'distinct_count_of_destinatio_nipa_ddress', + name: 'distinct_count_of_destination_ip_address', type: 'long', }, { @@ -8782,11 +19849,11 @@ export const filebeatSchema: Schema = [ type: 'long', }, { - name: 'selector_itd_otal_flows_observed', + name: 'selector_id_total_flows_observed', type: 'long', }, { - name: 'selector_itd_otal_flows_selected', + name: 'selector_id_total_flows_selected', type: 'long', }, { @@ -8950,7 +20017,7 @@ export const filebeatSchema: Schema = [ type: 'short', }, { - name: 'mib_object_valuei_pa_ddress', + name: 'mib_object_value_ip_address', type: 'ip', }, { @@ -9078,7 +20145,7 @@ export const filebeatSchema: Schema = [ type: 'long', }, { - name: 'max_bieb_ntries', + name: 'max_bib_entries', type: 'long', }, { @@ -9125,4 +20192,1052 @@ export const filebeatSchema: Schema = [ }, ], }, + { + 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/legacy/plugins/siem/server/utils/beat_schema/8.0.0/packetbeat.ts b/x-pack/legacy/plugins/siem/server/utils/beat_schema/8.0.0/packetbeat.ts index 6002c9370210e..0be2e48fe4668 100644 --- a/x-pack/legacy/plugins/siem/server/utils/beat_schema/8.0.0/packetbeat.ts +++ b/x-pack/legacy/plugins/siem/server/utils/beat_schema/8.0.0/packetbeat.ts @@ -15,91 +15,129 @@ export const packetbeatSchema: Schema = [ { key: 'ecs', title: 'ECS', - description: 'ECS fields.', + description: 'ECS Fields.', fields: [ { name: '@timestamp', - type: 'date', level: 'core', required: true, - example: '2016-05-23T08:05:34.853Z', + type: 'date', 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.', - }, - { - name: 'tags', - level: 'core', - type: 'keyword', - example: '["production", "env2"]', - description: 'List of keywords used to tag each event.', + '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', - example: { - env: 'production', - application: 'foo-bar', - }, + object_type: 'keyword', description: - '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.', + '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', - example: 'Hello World', description: - 'For log events the message field contains the log message. In other use cases the message field can be used to concatenate different values which are then freely searchable. If multiple messages exist, they can be combined into one message.', + '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 any, that collects, detects, or observes events on a host, or takes measurements on a host. Examples include Beats. Agents may also run on observers. ECS agent.* fields shall be populated with details of the agent running on the host or observer where the event happened or the measurement was taken.', + '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. For APM, it is the agent running in the app/service. The agent information does not change if data is sent through queuing systems like Kafka, Redis, or processing systems such as Logstash or APM Server.', + '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: 'version', + 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', - description: 'Version of the agent.', - example: '6.0.0-rc2', + 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: - '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.', + '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. 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.', + '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: 'id', + 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 identifier of this agent (if one exists). Example: For Beats this would be beat.id.', - example: '8a4f500d', + 'Unique number allocated to the autonomous system. The autonomous\nsystem number (ASN) uniquely identifies each network on the Internet.', + example: 15169, }, { - name: 'ephemeral_id', + name: 'organization.name', level: 'extended', type: 'keyword', - description: - 'Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but `agent.id` does not.', - example: '8a4f500f', + ignore_above: 1024, + multi_fields: [ + { + name: 'text', + type: 'text', + norms: false, + default_field: false, + }, + ], + description: 'Organization name.', + example: 'Google LLC', }, ], }, @@ -108,2673 +146,6183 @@ export const packetbeatSchema: Schema = [ title: 'Client', group: 2, description: - 'A client is defined as the initiator of a network connection for events regarding sessions, connections, or bidirectional flow records. For TCP events, the client is the initiator of the TCP connection that sends the SYN packet(s). For other protocols, the client is generally the initiator or requestor in the network transaction. Some systems use the term "originator" to refer the client in TCP connections. The client fields describe details about the system acting as the client in the network event. Client fields are usually populated in conjunction with server fields. Client fields are generally not populated for packet-level events. Client / server representations can add semantic context to an exchange, which is helpful to visualize the data in certain situations. If your context falls in that category, you should still ensure that source and destination are filled appropriately.', + '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 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.', + '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: 'ip', - level: 'core', - type: 'ip', - description: 'IP address of the client. Can be one or multiple IPv4 or IPv6 addresses.', + 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: 'port', + 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', - description: 'Port of the client.', + format: 'bytes', + description: 'Bytes sent from the client to the server.', + example: 184, }, { - name: 'mac', + name: 'domain', level: 'core', type: 'keyword', - description: 'MAC address of the client.', + ignore_above: 1024, + description: 'Client domain.', }, { - name: 'domain', + name: 'geo.city_name', level: 'core', type: 'keyword', - description: 'Client domain.', + ignore_above: 1024, + description: 'City name.', + example: 'Montreal', }, { - name: 'bytes', + name: 'geo.continent_name', level: 'core', - type: 'long', - format: 'bytes', - example: 184, - description: 'Bytes sent from the client to the server.', + type: 'keyword', + ignore_above: 1024, + description: 'Name of the continent.', + example: 'North America', }, { - name: 'packets', + name: 'geo.country_iso_code', level: 'core', - type: 'long', - example: 12, - description: 'Packets sent from the client to the server.', + type: 'keyword', + ignore_above: 1024, + description: 'Country ISO code.', + example: 'CA', }, { - name: 'geo', - title: 'Geo', - group: 2, - description: - 'Geo fields can carry data about a specific location related to an event or geo information derived from an IP field.', - type: 'group', - fields: [ - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - 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.country_name', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Country name.', + example: 'Canada', }, - ], - }, - { - name: 'cloud', - title: 'Cloud', - group: 2, - description: 'Fields related to the cloud or infrastructure the events are coming from.', - footnote: - 'Examples: If Metricbeat is running on an EC2 host and fetches data from its host, the cloud info contains the data about this machine. If Metricbeat runs on a remote machine outside the cloud and fetches data from a service running in the cloud, the field contains cloud data from the machine the service is running on.', - type: 'group', - fields: [ { - name: 'provider', + name: 'geo.location', + level: 'core', + type: 'geo_point', + description: 'Longitude and latitude.', + example: '{ "lon": -73.614830, "lat": 45.505918 }', + }, + { + name: 'geo.name', level: 'extended', - example: 'ec2', type: 'keyword', + ignore_above: 1024, description: - 'Name of the cloud provider. Example values are ec2, gce, or digitalocean.', + '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: 'availability_zone', - level: 'extended', - example: 'us-east-1c', + name: 'geo.region_iso_code', + level: 'core', type: 'keyword', - description: 'Availability zone in which this host is running.', + ignore_above: 1024, + description: 'Region ISO code.', + example: 'CA-QC', }, { - name: 'region', - level: 'extended', + name: 'geo.region_name', + level: 'core', type: 'keyword', - example: 'us-east-1', - description: 'Region in which this host is running.', + ignore_above: 1024, + description: 'Region name.', + example: 'Quebec', }, { - name: 'instance.id', - level: 'extended', - type: 'keyword', - example: 'i-1234567890abcdef0', - description: 'Instance ID of the host machine.', + name: 'ip', + level: 'core', + type: 'ip', + description: + 'IP address of the client.\n\nCan be one or multiple IPv4 or IPv6 addresses.', }, { - name: 'instance.name', - level: 'extended', + name: 'mac', + level: 'core', type: 'keyword', - description: 'Instance name of the host machine.', + ignore_above: 1024, + description: 'MAC address of the client.', }, { - name: 'machine.type', + name: 'nat.ip', level: 'extended', - type: 'keyword', - example: 't2.medium', - description: 'Machine type of the host machine.', + 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: 'account.id', + name: 'nat.port', level: 'extended', - type: 'keyword', - example: 666777888999, + type: 'long', + format: 'string', 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.', + 'Translated port of source based NAT sessions (e.g. internal client\nto internet).\n\nTypically connections traversing load balancers, firewalls, or routers.', }, - ], - }, - { - name: 'container', - title: 'Container', - group: 2, - description: - 'Container fields are used for meta information about the specific container that is the source of information. These fields help correlate data based containers from any runtime.', - type: 'group', - fields: [ { - name: 'runtime', - level: 'extended', - type: 'keyword', - description: 'Runtime managing this container.', - example: 'docker', + name: 'packets', + level: 'core', + type: 'long', + description: 'Packets sent from the client to the server.', + example: 12, }, { - name: 'id', + name: 'port', level: 'core', + type: 'long', + format: 'string', + description: 'Port of the client.', + }, + { + name: 'registered_domain', + level: 'extended', type: 'keyword', - description: 'Unique container id.', + 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: 'image.name', + name: 'top_level_domain', level: 'extended', type: 'keyword', - description: 'Name of the image the container was built on.', + 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: 'image.tag', + name: 'user.domain', level: 'extended', type: 'keyword', - description: 'Container image tag.', + 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: 'name', + name: 'user.email', level: 'extended', type: 'keyword', - description: 'Container name.', + ignore_above: 1024, + description: 'User email address.', }, { - name: 'labels', + name: 'user.full_name', level: 'extended', - type: 'object', - object_type: 'keyword', - description: 'Image labels.', + 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: 'destination', - title: 'Destination', - group: 2, - description: - 'Destination fields describe details about the destination of a packet/event. Destination fields are usually populated in conjunction with source fields.', - type: 'group', - fields: [ { - name: 'address', + name: 'user.group.domain', level: 'extended', type: 'keyword', + ignore_above: 1024, 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 of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', }, { - name: 'ip', - level: 'core', - type: 'ip', - description: - 'IP address of the destination. Can be one or multiple IPv4 or IPv6 addresses.', + name: 'user.group.id', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Unique identifier for the group on the system/platform.', }, { - name: 'port', - level: 'core', - type: 'long', - description: 'Port of the destination.', + name: 'user.group.name', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Name of the group.', }, { - name: 'mac', - level: 'core', + name: 'user.hash', + level: 'extended', type: 'keyword', - description: 'MAC address of the destination.', + 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: 'domain', + name: 'user.id', level: 'core', type: 'keyword', - description: 'Destination domain.', + ignore_above: 1024, + description: 'Unique identifiers of the user.', }, { - name: 'bytes', + name: 'user.name', level: 'core', - type: 'long', - format: 'bytes', - example: 184, - description: 'Bytes sent from the destination to the source.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - example: 12, - description: 'Packets sent from the destination to the source.', - }, - { - name: 'geo', - title: 'Geo', - group: 2, - description: - 'Geo fields can carry data about a specific location related to an event or geo information derived from an IP field.', - type: 'group', - fields: [ - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, + type: 'keyword', + ignore_above: 1024, + multi_fields: [ { - name: 'name', - level: 'extended', - type: 'keyword', - 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: 'text', + type: 'text', + norms: false, + default_field: false, }, ], + description: 'Short name or login of the user.', + example: 'albert', }, ], }, { - name: 'ecs', - title: 'ECS', + name: 'cloud', + title: 'Cloud', group: 2, - description: 'Meta-information specific to ECS.', + 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: 'version', - level: 'core', + name: 'account.id', + level: 'extended', type: 'keyword', - required: true, + ignore_above: 1024, 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. The current version is 1.0.0-beta2 .', - example: '1.0.0-beta2', + '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: 'error', - title: 'Error', - group: 2, - description: - 'These fields can represent errors of any kind. Use them for errors that happen while fetching events or in cases where the event itself contains an error.', - type: 'group', - fields: [ { - name: 'id', - level: 'core', + name: 'availability_zone', + level: 'extended', type: 'keyword', - description: 'Unique identifier for the error.', + ignore_above: 1024, + description: 'Availability zone in which this host is running.', + example: 'us-east-1c', }, { - name: 'message', - level: 'core', - type: 'text', - description: 'Error message.', + name: 'instance.id', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Instance ID of the host machine.', + example: 'i-1234567890abcdef0', }, { - name: 'code', - level: 'core', + name: 'instance.name', + level: 'extended', type: 'keyword', - description: 'Error code describing the error.', + ignore_above: 1024, + description: 'Instance name of the host machine.', }, - ], - }, - { - name: 'event', - title: 'Event', - group: 2, - description: - 'The event fields are used for context information about the log or metric event itself. A log is defined as an event containing details of something that happened. Log events must include the time at which the thing happened. Examples of log events include a process starting on a host, a network packet being sent from a source to a destination, or a network connection between a client and a server being initiated or closed. A metric is defined as an event containing one or more numerical or categorical measurements and the time at which the measurement was taken. Examples of metric events include memory pressure measured on a host, or vulnerabilities measured on a scanned host.', - type: 'group', - fields: [ { - name: 'id', - level: 'core', + name: 'machine.type', + level: 'extended', type: 'keyword', - description: 'Unique ID to describe the event.', - example: '8a4f500d', + ignore_above: 1024, + description: 'Machine type of the host machine.', + example: 't2.medium', }, { - name: 'kind', + name: 'provider', level: 'extended', type: 'keyword', + ignore_above: 1024, description: - 'The kind of the event. This gives information about what type of information the event contains, without being specific to the contents of the event. Examples are `event`, `state`, `alarm`. Warning: In future versions of ECS, we plan to provide a list of acceptable values for this field, please use with caution.', - example: 'state', + 'Name of the cloud provider. Example values are aws, azure, gcp,\nor digitalocean.', + example: 'aws', }, { - name: 'category', - level: 'core', + name: 'region', + level: 'extended', type: 'keyword', - description: - 'Event category. This contains high-level information about the contents of the event. It is more generic than `event.action`, in the sense that typically a category contains multiple actions. Warning: In future versions of ECS, we plan to provide a list of acceptable values for this field, please use with caution.', - example: 'user-management', + 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: 'action', + name: 'exists', level: 'core', - type: 'keyword', - 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', + type: 'boolean', + description: 'Boolean to capture if a signature is present.', + example: 'true', + default_field: false, }, { - name: 'outcome', + name: 'status', level: 'extended', type: 'keyword', + ignore_above: 1024, description: - 'The outcome of the event. If the event describes an action, this fields contains the outcome of that action. Examples outcomes are `success` and `failure`. Warning: In future versions of ECS, we plan to provide a list of acceptable values for this field, please use with caution.', - example: 'success', - }, - { - name: 'type', - level: 'core', - type: 'keyword', - description: 'Reserved for future usage. Please avoid using this field for user data.', + '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: 'module', + name: 'subject_name', level: 'core', type: 'keyword', - description: - 'Name of the module this data is coming from. This information is coming from the modules used in Beats or Logstash.', - example: 'mysql', + ignore_above: 1024, + description: 'Subject name of the code signer', + example: 'Microsoft Corporation', + default_field: false, }, { - name: 'dataset', - level: 'core', - type: 'keyword', + name: 'trusted', + level: 'extended', + type: 'boolean', description: - 'Name of the dataset. The concept of a `dataset` (fileset / metricset) is used in Beats as a subset of modules. It contains the information which is currently stored in metricset.name and metricset.module or fileset.name.', - example: 'stats', + '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: 'severity', - level: 'core', - type: 'long', - example: '7', + name: 'valid', + level: 'extended', + type: 'boolean', description: - "Severity describes the severity of the event. What the different severity values mean can very different between use cases. It's up to the implementer to make sure severities are consistent across events. ", + '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: 'original', + name: 'id', level: 'core', type: 'keyword', - 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', - 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`.', - index: false, - doc_values: false, + ignore_above: 1024, + description: 'Unique container id.', }, { - name: 'hash', + name: 'image.name', level: 'extended', type: 'keyword', - example: '123456789012345678901234567890ABCD', - description: - 'Hash (perhaps logstash fingerprint) of raw field to be able to demonstrate log integrity.', - }, - { - name: 'duration', - level: 'core', - type: 'long', - format: 'duration', - input_format: 'nanoseconds', - 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.', + ignore_above: 1024, + description: 'Name of the image the container was built on.', }, { - name: 'timezone', + name: 'image.tag', level: 'extended', type: 'keyword', - 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").', + ignore_above: 1024, + description: 'Container image tags.', }, { - name: 'created', - level: 'core', - type: 'date', - description: - 'event.created contains the date when the event was created. This timestamp is distinct from @timestamp in that @timestamp contains the processed timestamp. For logs these two timestamps can be different as the timestamp in the log line and when the event is read for example by Filebeat are not identical. `@timestamp` must contain the timestamp extracted from the log line, event.created when the log line is read. The same could apply to package capturing where @timestamp contains the timestamp extracted from the network package and event.created when the event was created. In case the two timestamps are identical, @timestamp should be used.', - }, - { - name: 'start', + name: 'labels', level: 'extended', - type: 'date', - description: - 'event.start contains the date when the event started or when the activity was first observed.', + type: 'object', + object_type: 'keyword', + description: 'Image labels.', }, { - name: 'end', + name: 'name', level: 'extended', - type: 'date', - description: - 'event.end contains the date when the event ended or when the activity was last observed.', - }, - { - name: 'risk_score', - level: 'core', - type: 'float', - description: - "Risk score or priority of the event (e.g. security solutions). Use your system's original value here. ", + type: 'keyword', + ignore_above: 1024, + description: 'Container name.', }, { - name: 'risk_score_norm', + name: 'runtime', level: 'extended', - type: 'float', - 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.', + type: 'keyword', + ignore_above: 1024, + description: 'Runtime managing this container.', + example: 'docker', }, ], }, { - name: 'file', + name: 'destination', + title: 'Destination', group: 2, - title: 'File', description: - 'A file is defined as a set of information that has been created on, or has existed on a filesystem. File objects can be associated with host events, network events, and/or file events (e.g., those produced by File Integrity Monitoring [FIM] products or services). File fields provide details about the affected file associated with the event or metric.', + '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: 'path', + name: 'address', level: 'extended', type: 'keyword', - description: 'Path to the file.', + 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: 'target_path', + name: 'as.number', level: 'extended', - type: 'keyword', - description: 'Target path for symlinks.', + 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: 'extension', + name: 'as.organization.name', level: 'extended', type: 'keyword', - description: 'File extension. This should allow easy filtering by file extensions.', - example: 'png', + ignore_above: 1024, + multi_fields: [ + { + name: 'text', + type: 'text', + norms: false, + default_field: false, + }, + ], + description: 'Organization name.', + example: 'Google LLC', }, { - name: 'type', - level: 'extended', - type: 'keyword', - description: 'File type (file, dir, or symlink).', + name: 'bytes', + level: 'core', + type: 'long', + format: 'bytes', + description: 'Bytes sent from the destination to the source.', + example: 184, }, { - name: 'device', - level: 'extended', + name: 'domain', + level: 'core', type: 'keyword', - description: 'Device that is the source of the file.', + ignore_above: 1024, + description: 'Destination domain.', }, { - name: 'inode', - level: 'extended', + name: 'geo.city_name', + level: 'core', type: 'keyword', - description: 'Inode representing the file in the filesystem.', + ignore_above: 1024, + description: 'City name.', + example: 'Montreal', }, { - name: 'uid', - level: 'extended', + name: 'geo.continent_name', + level: 'core', type: 'keyword', - description: 'The user ID (UID) or security identifier (SID) of the file owner.', + ignore_above: 1024, + description: 'Name of the continent.', + example: 'North America', }, { - name: 'owner', - level: 'extended', + name: 'geo.country_iso_code', + level: 'core', type: 'keyword', - description: "File owner's username.", + ignore_above: 1024, + description: 'Country ISO code.', + example: 'CA', }, { - name: 'gid', - level: 'extended', + name: 'geo.country_name', + level: 'core', type: 'keyword', - description: 'Primary group ID (GID) of the file.', + ignore_above: 1024, + description: 'Country name.', + example: 'Canada', }, { - name: 'group', - level: 'extended', - type: 'keyword', - description: 'Primary group name of the file.', + name: 'geo.location', + level: 'core', + type: 'geo_point', + description: 'Longitude and latitude.', + example: '{ "lon": -73.614830, "lat": 45.505918 }', }, { - name: 'mode', - level: 'extended', - type: 'keyword', - example: 416, - description: 'Mode of the file in octal representation.', - }, - { - name: 'size', - level: 'extended', - type: 'long', - format: 'bytes', - description: 'File size in bytes (field is only added when `type` is `file`).', - }, - { - name: 'mtime', - level: 'extended', - type: 'date', - description: 'Last time file content was modified.', - }, - { - name: 'ctime', - level: 'extended', - type: 'date', - description: 'Last time file metadata changed.', - }, - ], - }, - { - name: 'group', - title: 'Group', - group: 2, - description: - 'The group fields are meant to represent groups that are relevant to the event.', - type: 'group', - fields: [ - { - name: 'id', + name: 'geo.name', 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. ECS host.* fields should be populated with details about the host on which the event happened, or on which the measurement was taken. Host types include hardware, virtual machines, Docker containers, and Kubernetes nodes.', - type: 'group', - fields: [ - { - name: 'hostname', - level: 'core', - type: 'keyword', + ignore_above: 1024, description: - 'Hostname of the host. It normally contains what the `hostname` command returns on the host machine.', + '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: 'name', + name: 'geo.region_iso_code', level: 'core', type: 'keyword', - 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.', + ignore_above: 1024, + description: 'Region ISO code.', + example: 'CA-QC', }, { - name: 'id', + name: 'geo.region_name', level: 'core', type: 'keyword', - 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`.', + ignore_above: 1024, + description: 'Region name.', + example: 'Quebec', }, { name: 'ip', level: 'core', type: 'ip', - description: 'Host ip address.', + description: + 'IP address of the destination.\n\nCan be one or multiple IPv4 or IPv6 addresses.', }, { name: 'mac', level: 'core', type: 'keyword', - description: 'Host mac address.', + ignore_above: 1024, + description: 'MAC address of the destination.', }, { - name: 'type', - level: 'core', - type: 'keyword', + name: 'nat.ip', + level: 'extended', + type: 'ip', 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.', + 'Translated ip of destination based NAT sessions (e.g. internet\nto private DMZ)\n\nTypically used with load balancers, firewalls, or routers.', }, { - name: 'architecture', - level: 'core', - type: 'keyword', - example: 'x86_64', - description: 'Operating system architecture.', + 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: 'os', - title: 'Operating System', - group: 2, - description: 'The OS fields contain information about the operating system.', - reusable: { - top_level: false, - expected: ['observer', 'host', 'user_agent'], - }, - type: 'group', - fields: [ - { - name: 'platform', - level: 'extended', - type: 'keyword', - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - example: 'Mac OS X', - description: 'Operating system name, without the version.', - }, - { - name: 'full', - level: 'extended', - type: 'keyword', - example: 'Mac OS Mojave', - description: 'Operating system name, including the version or code name.', - }, - { - name: 'family', - level: 'extended', - type: 'keyword', - example: 'debian', - description: 'OS family (such as redhat, debian, freebsd, windows).', - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - example: '10.14.1', - description: 'Operating system version as a raw string.', - }, - { - name: 'kernel', - level: 'extended', - type: 'keyword', - example: '4.4.0-112-generic', - description: 'Operating system kernel version as a raw string.', - }, - ], + name: 'packets', + level: 'core', + type: 'long', + description: 'Packets sent from the destination to the source.', + example: 12, }, { - name: 'geo', - title: 'Geo', - group: 2, - description: - 'Geo fields can carry data about a specific location related to an event or geo information derived from an IP field.', - type: 'group', - fields: [ - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - 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: 'port', + level: 'core', + type: 'long', + format: 'string', + description: 'Port of the destination.', }, - ], - }, - { - name: 'http', - title: 'HTTP', - group: 2, - description: 'Fields related to HTTP activity.', - type: 'group', - fields: [ { - name: 'request.method', + name: 'registered_domain', level: 'extended', type: 'keyword', + ignore_above: 1024, description: - 'Http request method. The field value must be normalized to lowercase for querying. See "Lowercase Capitalization" in the "Implementing ECS" section.', - example: 'get, post, put', + '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: 'request.body.content', + name: 'top_level_domain', level: 'extended', type: 'keyword', - description: 'The full http request body.', - example: 'Hello world', + 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: 'request.referrer', + name: 'user.domain', level: 'extended', type: 'keyword', - description: 'Referrer for this HTTP request.', - example: 'https://blog.example.com/', - }, - { - name: 'response.status_code', - level: 'extended', - type: 'long', - description: 'Http response status code.', - example: 404, + 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: 'response.body.content', + name: 'user.email', level: 'extended', type: 'keyword', - description: 'The full http response body.', - example: 'Hello world', + ignore_above: 1024, + description: 'User email address.', }, { - name: 'version', + name: 'user.full_name', level: 'extended', type: 'keyword', - description: 'Http version.', - example: 1.1, + 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: 'request.bytes', + name: 'user.group.domain', level: 'extended', - type: 'long', - format: 'bytes', - description: 'Total size in bytes of the request (body and headers).', - example: 1437, + 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: 'request.body.bytes', + name: 'user.group.id', level: 'extended', - type: 'long', - format: 'bytes', - description: 'Size in bytes of the request body.', - example: 887, + type: 'keyword', + ignore_above: 1024, + description: 'Unique identifier for the group on the system/platform.', }, { - name: 'response.bytes', + name: 'user.group.name', level: 'extended', - type: 'long', - format: 'bytes', - description: 'Total size in bytes of the response (body and headers).', - example: 1437, + type: 'keyword', + ignore_above: 1024, + description: 'Name of the group.', }, { - name: 'response.body.bytes', + name: 'user.hash', level: 'extended', - type: 'long', - format: 'bytes', - description: 'Size in bytes of the response body.', - example: 887, + 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: 'log', - title: 'Log', - description: 'Fields which are specific to log events.', - type: 'group', - fields: [ { - name: 'level', + name: 'user.id', level: 'core', type: 'keyword', - description: 'Log level of the log event. Some examples are `WARN`, `ERR`, `INFO`.', - example: 'ERR', + ignore_above: 1024, + description: 'Unique identifiers of the user.', }, { - name: 'original', + name: 'user.name', level: 'core', type: 'keyword', - example: 'Sep 19 08:26:10 localhost My log', - index: false, - doc_values: false, - 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`. ", + 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: 'network', - title: 'Network', + name: 'dll', + title: 'DLL', group: 2, description: - 'The network is defined as the communication path over which a host or network event happens. The network.* fields should be populated with details about the network activity associated with an event.', + '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: 'name', + 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', - description: 'Name given by operators to sections of their network.', - example: 'Guest Wifi', + 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: 'type', + 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: - '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 "Lowercase Capitalization" in the "Implementing ECS" section.', - example: 'ipv4', + '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: 'iana_number', + name: 'code_signature.valid', level: 'extended', - type: 'keyword', + type: 'boolean', 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, + '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: 'transport', - level: 'core', + name: 'hash.md5', + level: 'extended', type: 'keyword', - 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 "Lowercase Capitalization" in the "Implementing ECS" section.', - example: 'tcp', + ignore_above: 1024, + description: 'MD5 hash.', + default_field: false, }, { - name: 'application', + name: 'hash.sha1', level: 'extended', type: 'keyword', - description: - 'A name given to an application. 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 "Lowercase Capitalization" in the "Implementing ECS" section.', - example: 'aim', + ignore_above: 1024, + description: 'SHA1 hash.', + default_field: false, }, { - name: 'protocol', - level: 'core', + name: 'hash.sha256', + level: 'extended', type: 'keyword', - description: - 'L7 Network protocol name. ex. http, lumberjack, transport protocol. The field value must be normalized to lowercase for querying. See "Lowercase Capitalization" in the "Implementing ECS" section.', - example: 'http', + ignore_above: 1024, + description: 'SHA256 hash.', + default_field: false, }, { - name: 'direction', - level: 'core', + name: 'hash.sha512', + level: 'extended', 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', + ignore_above: 1024, + description: 'SHA512 hash.', + default_field: false, }, { - name: 'forwarded_ip', + name: 'name', level: 'core', - type: 'ip', - description: 'Host IP address when the source IP address is the proxy.', - example: '192.1.1.2', + 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: 'community_id', + name: 'path', level: 'extended', type: 'keyword', - 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: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - description: - 'Total bytes transferred in both directions. If `source.bytes` and `destination.bytes` are known, `network.bytes` is their sum.', - example: 368, - }, - { - name: 'packets', - level: 'core', - type: 'long', - description: - 'Total packets transferred in both directions. If `source.packets` and `destination.packets` are known, `network.packets` is their sum.', - example: 24, - }, - ], - }, - { - name: 'observer', - title: 'Observer', - group: 2, - description: - 'An observer is defined as a special network, security, or application device used to detect, observe, or create network, security, or application-related events and metrics. This could be a custom hardware appliance or a server that has been configured to run special network, security, or application software. Examples include firewalls, intrusion detection/prevention systems, network monitoring sensors, web application firewalls, data loss prevention systems, and APM servers. The observer.* fields shall be populated with details of the system, if any, that detects, observes and/or creates a network, security, or application event or metric. Message queues and ETL components used in processing events or metrics are not considered observers in ECS.', - type: 'group', - fields: [ - { - name: 'mac', - level: 'core', - type: 'keyword', - description: 'MAC address of the observer', - }, - { - name: 'ip', - level: 'core', - type: 'ip', - description: 'IP address of the observer.', + ignore_above: 1024, + description: 'Full file path of the library.', + example: 'C:\\Windows\\System32\\kernel32.dll', + default_field: false, }, { - name: 'hostname', - level: 'core', + name: 'pe.company', + level: 'extended', type: 'keyword', - description: 'Hostname of the observer.', + ignore_above: 1024, + description: 'Internal company name of the file, provided at compile-time.', + example: 'Microsoft Corporation', + default_field: false, }, { - name: 'vendor', - level: 'core', + name: 'pe.description', + level: 'extended', type: 'keyword', - description: 'observer vendor information.', + ignore_above: 1024, + description: 'Internal description of the file, provided at compile-time.', + example: 'Paint', + default_field: false, }, { - name: 'version', - level: 'core', + name: 'pe.file_version', + level: 'extended', type: 'keyword', - description: 'Observer version.', + ignore_above: 1024, + description: 'Internal version of the file, provided at compile-time.', + example: '6.3.9600.17415', + default_field: false, }, { - name: 'serial_number', + name: 'pe.original_file_name', level: 'extended', type: 'keyword', - description: 'Observer serial number.', + ignore_above: 1024, + description: 'Internal name of the file, provided at compile-time.', + example: 'MSPAINT.EXE', + default_field: false, }, { - name: 'type', - level: 'core', + name: 'pe.product', + level: 'extended', type: 'keyword', - 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: 'os', - title: 'Operating System', - group: 2, - description: 'The OS fields contain information about the operating system.', - reusable: { - top_level: false, - expected: ['observer', 'host', 'user_agent'], - }, - type: 'group', - fields: [ - { - name: 'platform', - level: 'extended', - type: 'keyword', - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - example: 'Mac OS X', - description: 'Operating system name, without the version.', - }, - { - name: 'full', - level: 'extended', - type: 'keyword', - example: 'Mac OS Mojave', - description: 'Operating system name, including the version or code name.', - }, - { - name: 'family', - level: 'extended', - type: 'keyword', - example: 'debian', - description: 'OS family (such as redhat, debian, freebsd, windows).', - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - example: '10.14.1', - description: 'Operating system version as a raw string.', - }, - { - name: 'kernel', - level: 'extended', - type: 'keyword', - example: '4.4.0-112-generic', - description: 'Operating system kernel version as a raw string.', - }, - ], - }, - { - name: 'geo', - title: 'Geo', - group: 2, - description: - 'Geo fields can carry data about a specific location related to an event or geo information derived from an IP field.', - type: 'group', - fields: [ - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - 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', - }, - ], + ignore_above: 1024, + description: 'Internal product name of the file, provided at compile-time.', + example: 'Microsoft® Windows® Operating System', + default_field: false, }, ], }, { - name: 'organization', - title: 'Organization', + name: 'dns', + title: 'DNS', group: 2, description: - 'The organization fields enrich data with information about the company or entity the data is associated with. These fields help you arrange or filter data stored in an index by one or multiple organizations.', + '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: 'name', + name: 'answers', level: 'extended', - type: 'keyword', - description: 'Organization name.', + 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: 'id', + name: 'answers.class', level: 'extended', type: 'keyword', - description: 'Unique identifier for the organization.', + ignore_above: 1024, + description: 'The class of DNS data contained in this resource record.', + example: 'IN', }, - ], - }, - { - name: 'os', - title: 'Operating System', - group: 2, - description: 'The OS fields contain information about the operating system.', - reusable: { - top_level: false, - expected: ['observer', 'host', 'user_agent'], - }, - type: 'group', - fields: [ { - name: 'platform', + name: 'answers.data', level: 'extended', type: 'keyword', - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', + 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: 'name', + name: 'answers.name', level: 'extended', type: 'keyword', - example: 'Mac OS X', - description: 'Operating system name, without the version.', + 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: 'full', + name: 'answers.ttl', level: 'extended', - type: 'keyword', - example: 'Mac OS Mojave', - description: 'Operating system name, including the version or code name.', + 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: 'family', + name: 'answers.type', level: 'extended', type: 'keyword', - example: 'debian', - description: 'OS family (such as redhat, debian, freebsd, windows).', + ignore_above: 1024, + description: 'The type of data contained in this resource record.', + example: 'CNAME', }, { - name: 'version', + name: 'header_flags', level: 'extended', type: 'keyword', - example: '10.14.1', - description: 'Operating system version as a raw string.', + 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: 'kernel', + name: 'id', level: 'extended', type: 'keyword', - example: '4.4.0-112-generic', - description: 'Operating system kernel version as a raw string.', + 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: 'process', - title: 'Process', - group: 2, - description: - 'These fields contain information about a process. These fields can help you correlate metrics information with a process id/name from a log message. The `process.pid` often stays in the metric itself and is copied to the global field for correlation.', - type: 'group', - fields: [ { - name: 'pid', - level: 'core', - type: 'long', - description: 'Process id.', - example: 'ssh', + 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: 'name', + name: 'question.class', level: 'extended', type: 'keyword', - description: 'Process name. Sometimes called program name or similar.', - example: 'ssh', + ignore_above: 1024, + description: 'The class of records being queried.', + example: 'IN', }, { - name: 'ppid', + name: 'question.name', level: 'extended', - type: 'long', - description: 'Process parent id.', + 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: 'args', + name: 'question.registered_domain', level: 'extended', type: 'keyword', - description: 'Process arguments. May be filtered to protect sensitive information.', - example: ['ssh', '-l', 'user', '10.0.0.16'], + 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: 'executable', + name: 'question.subdomain', level: 'extended', type: 'keyword', - description: 'Absolute path to the process executable.', - example: '/usr/bin/ssh', + 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: 'title', + name: 'question.top_level_domain', level: 'extended', type: 'keyword', + ignore_above: 1024, 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.', + '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: 'thread.id', + name: 'question.type', level: 'extended', - type: 'long', - example: 4242, - description: 'Thread ID.', + type: 'keyword', + ignore_above: 1024, + description: 'The type of record being queried.', + example: 'AAAA', }, { - name: 'start', + name: 'resolved_ip', level: 'extended', - type: 'date', - example: '2016-05-23T08:05:34.853Z', - description: 'The time the process started.', + 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: 'working_directory', + name: 'response_code', level: 'extended', type: 'keyword', - example: '/home/alice', - description: 'The working directory of the process.', + 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: 'related', - title: 'Related', + name: 'ecs', + title: 'ECS', group: 2, - description: - 'This field set is meant to facilitate pivoting around a piece of data. Some pieces of information can be seen in many places in ECS. To facilitate searching for them, append values to their corresponding field in `related.`. A concrete example is IP addresses, which can be under host, observer, source, destination, client, server, and network.forwarded_ip. If you append all IPs to `related.ip`, you can then search for a given IP trivially, no matter where it appeared, by querying `related.ip:a.b.c.d`.', + description: 'Meta-information specific to ECS.', type: 'group', fields: [ { - name: 'ip', - level: 'extended', - type: 'ip', - description: 'All of the IPs seen on your event.', + 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: 'server', - title: 'Server', + name: 'error', + title: 'Error', group: 2, description: - 'A Server is defined as the responder in a network connection for events regarding sessions, connections, or bidirectional flow records. For TCP events, the server is the receiver of the initial SYN packet(s) of the TCP connection. For other protocols, the server is generally the responder in the network transaction. Some systems actually use the term "responder" to refer the server in TCP connections. The server fields describe details about the system acting as the server in the network event. Server fields are usually populated in conjunction with client fields. Server fields are generally not populated for packet-level events. Client / server representations can add semantic context to an exchange, which is helpful to visualize the data in certain situations. If your context falls in that category, you should still ensure that source and destination are filled appropriately.', + '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: 'address', - level: 'extended', + name: 'code', + level: 'core', type: 'keyword', - 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.', + ignore_above: 1024, + description: 'Error code describing the error.', }, { - name: 'ip', + name: 'id', level: 'core', - type: 'ip', - description: 'IP address of the server. Can be one or multiple IPv4 or IPv6 addresses.', + type: 'keyword', + ignore_above: 1024, + description: 'Unique identifier for the error.', }, { - name: 'port', + name: 'message', level: 'core', - type: 'long', - description: 'Port of the server.', - }, - { - name: 'mac', - level: 'core', - type: 'keyword', - description: 'MAC address of the server.', + type: 'text', + description: 'Error message.', }, { - name: 'domain', - level: 'core', + name: 'stack_trace', + level: 'extended', type: 'keyword', - description: 'Server domain.', - }, - { - name: 'bytes', - level: 'core', - type: 'long', - format: 'bytes', - example: 184, - description: 'Bytes sent from the server to the client.', - }, - { - name: 'packets', - level: 'core', - type: 'long', - example: 12, - description: 'Packets sent from the server to the client.', - }, - { - name: 'geo', - title: 'Geo', - group: 2, - description: - 'Geo fields can carry data about a specific location related to an event or geo information derived from an IP field.', - type: 'group', - fields: [ - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, + ignore_above: 1024, + multi_fields: [ { - name: 'name', - level: 'extended', - type: 'keyword', - 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: '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: 'service', - title: 'Service', + name: 'event', + title: 'Event', group: 2, description: - 'The service fields describe the service for or from which the data was collected. These fields help you find and correlate logs for a specific service and version.', + '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: 'id', + name: 'action', level: 'core', type: 'keyword', + ignore_above: 1024, description: - 'Unique identifier of the running service. This id should uniquely identify this service. This makes it possible to correlate logs and metrics for one specific service. Example: If you are experiencing issues with one redis instance, you can filter on that id to see metrics and logs for that single instance.', - example: 'd37e5ebfe0ae6c4972dbe9f0174a1637bb8247f6', + '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: 'name', + name: 'category', level: 'core', type: 'keyword', - example: 'elasticsearch-metrics', + ignore_above: 1024, description: - 'Name of the service data is collected from. The name of the service is normally user given. This allows if two instances of the same service are running on the same machine they can be differentiated by the `service.name`. Also it 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.', + '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: 'type', - level: 'core', + name: 'code', + level: 'extended', type: 'keyword', - example: 'elasticsearch', + ignore_above: 1024, 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`.', + '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: 'state', + name: 'created', level: 'core', - type: 'keyword', - description: 'Current state of the service.', + 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: 'version', + name: 'dataset', level: 'core', type: 'keyword', - example: '3.2.4', + ignore_above: 1024, 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.', + '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: 'ephemeral_id', + 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: 'keyword', + type: 'date', description: - 'Ephemeral identifier of this service (if one exists). This id normally changes across restarts, but `service.id` does not.', - example: '8a4f500f', + 'event.end contains the date when the event ended or when the activity\nwas last observed.', }, - ], - }, - { - name: 'source', - title: 'Source', - group: 2, - description: - 'Source fields describe details about the source of a packet/event. Source fields are usually populated in conjunction with destination fields.', - type: 'group', - fields: [ { - name: 'address', + name: 'hash', level: 'extended', type: 'keyword', + ignore_above: 1024, 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.', + 'Hash (perhaps logstash fingerprint) of raw field to be able to\ndemonstrate log integrity.', + example: '123456789012345678901234567890ABCD', }, { - name: 'ip', + name: 'id', level: 'core', - type: 'ip', - description: 'IP address of the source. Can be one or multiple IPv4 or IPv6 addresses.', + type: 'keyword', + ignore_above: 1024, + description: 'Unique ID to describe the event.', + example: '8a4f500d', }, { - name: 'port', + name: 'ingested', level: 'core', - type: 'long', - description: 'Port of the source.', + 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: 'mac', + name: 'kind', level: 'core', type: 'keyword', - description: 'MAC address of the source.', + 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: 'domain', + name: 'module', level: 'core', type: 'keyword', - description: 'Source domain.', + 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: 'bytes', + name: 'original', level: 'core', - type: 'long', - format: 'bytes', - example: 184, - description: 'Bytes sent from the source to the destination.', + 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: 'packets', + name: 'outcome', level: 'core', - type: 'long', - example: 12, - description: 'Packets sent from the source to the destination.', - }, - { - name: 'geo', - title: 'Geo', - group: 2, + type: 'keyword', + ignore_above: 1024, description: - 'Geo fields can carry data about a specific location related to an event or geo information derived from an IP field.', - type: 'group', - fields: [ - { - name: 'location', - level: 'core', - type: 'geo_point', - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - }, - { - name: 'continent_name', - level: 'core', - type: 'keyword', - description: 'Name of the continent.', - example: 'North America', - }, - { - name: 'country_name', - level: 'core', - type: 'keyword', - description: 'Country name.', - example: 'Canada', - }, - { - name: 'region_name', - level: 'core', - type: 'keyword', - description: 'Region name.', - example: 'Quebec', - }, - { - name: 'city_name', - level: 'core', - type: 'keyword', - description: 'City name.', - example: 'Montreal', - }, - { - name: 'country_iso_code', - level: 'core', - type: 'keyword', - description: 'Country ISO code.', - example: 'CA', - }, - { - name: 'region_iso_code', - level: 'core', - type: 'keyword', - description: 'Region ISO code.', - example: 'CA-QC', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - 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', - }, - ], + '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: 'url', - title: 'URL', - description: 'URL fields provide a complete URL, with scheme, host, and path.', - type: 'group', - fields: [ { - name: 'original', + name: 'provider', level: 'extended', type: 'keyword', + ignore_above: 1024, 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', + '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: 'full', + name: 'reference', level: 'extended', type: 'keyword', + ignore_above: 1024, 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', + '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: 'scheme', - level: 'extended', - type: 'keyword', + name: 'risk_score', + level: 'core', + type: 'float', description: - 'Scheme of the request, such as "https". Note: The `:` is not part of the scheme.', - example: 'https', + "Risk score or priority of the event (e.g. security solutions).\nUse your system's original value here.", }, { - name: 'domain', + name: 'risk_score_norm', level: 'extended', - type: 'keyword', + type: 'float', description: - 'Domain of the request, 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', + '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: 'port', + name: 'sequence', level: 'extended', - type: 'integer', - description: 'Port of the request, such as 443.', - example: 443, + 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: 'path', - level: 'extended', - type: 'keyword', - description: 'Path of the request, such as "/search".', + 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: 'query', + name: 'start', level: 'extended', - type: 'keyword', + type: 'date', 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.', + 'event.start contains the date when the event started or when the\nactivity was first observed.', }, { - name: 'fragment', + name: 'timezone', level: 'extended', type: 'keyword', + ignore_above: 1024, description: - 'Portion of the url after the `#`, such as "top". The `#` is not part of the fragment.', + '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: 'username', - level: 'extended', + name: 'type', + level: 'core', type: 'keyword', - description: 'Username of the request.', + 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: 'password', + name: 'url', level: 'extended', type: 'keyword', - description: 'Password of the request.', + 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: 'user', - title: 'User', + name: 'file', + title: 'File', group: 2, description: - 'The user fields describe information about the user that is relevant to the event. Fields can have one entry or multiple entries. If a user has more than one id, provide an array that includes all of them.', - reusable: { - top_level: true, - expected: ['client', 'destination', 'host', 'server', 'source'], - }, + '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: 'id', - level: 'core', + 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', - description: 'One or multiple unique identifiers of the user.', + 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: 'name', + name: 'code_signature.exists', level: 'core', - type: 'keyword', - example: 'albert', - description: 'Short name or login of the user.', + type: 'boolean', + description: 'Boolean to capture if a signature is present.', + example: 'true', + default_field: false, }, { - name: 'full_name', + name: 'code_signature.status', level: 'extended', type: 'keyword', - example: 'Albert Einstein', - description: "User's full name, if available. ", + 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: 'email', - level: 'extended', + name: 'code_signature.subject_name', + level: 'core', type: 'keyword', - description: 'User email address.', + ignore_above: 1024, + description: 'Subject name of the code signer', + example: 'Microsoft Corporation', + default_field: false, }, { - name: 'hash', + name: 'code_signature.trusted', level: 'extended', - type: 'keyword', + type: 'boolean', 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.', + '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: 'group', - title: 'Group', - group: 2, + name: 'code_signature.valid', + level: 'extended', + type: 'boolean', description: - 'The group fields are meant to represent groups that are relevant to 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.', - }, - ], + '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: 'user_agent', - title: 'User agent', - group: 2, - description: - 'The user_agent fields normally come from a browser request. They often show up in web service logs coming from the parsed user agent string.', - type: 'group', - fields: [ { - name: 'original', + name: 'created', 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 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1', + type: 'date', + description: + 'File creation time.\n\nNote that not all filesystems store the creation time.', }, { - name: 'name', + name: 'ctime', level: 'extended', - type: 'keyword', - example: 'Safari', - description: 'Name of the user agent.', + 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: 'version', + name: 'device', level: 'extended', type: 'keyword', - description: 'Version of the user agent.', - example: 12, + ignore_above: 1024, + description: 'Device that is the source of the file.', + example: 'sda', }, { - name: 'device.name', + name: 'directory', level: 'extended', type: 'keyword', - example: 'iPhone', - description: 'Name of the device.', + ignore_above: 1024, + description: + 'Directory where the file is located. It should include the drive\nletter, when appropriate.', + example: '/home/alice', }, { - name: 'os', - title: 'Operating System', - group: 2, - description: 'The OS fields contain information about the operating system.', - reusable: { - top_level: false, - expected: ['observer', 'host', 'user_agent'], - }, - type: 'group', - fields: [ - { - name: 'platform', - level: 'extended', - type: 'keyword', - description: 'Operating system platform (such centos, ubuntu, windows).', - example: 'darwin', - }, - { - name: 'name', - level: 'extended', - type: 'keyword', - example: 'Mac OS X', - description: 'Operating system name, without the version.', - }, - { - name: 'full', - level: 'extended', - type: 'keyword', - example: 'Mac OS Mojave', - description: 'Operating system name, including the version or code name.', - }, - { - name: 'family', - level: 'extended', - type: 'keyword', - example: 'debian', - description: 'OS family (such as redhat, debian, freebsd, windows).', - }, - { - name: 'version', - level: 'extended', - type: 'keyword', - example: '10.14.1', - description: 'Operating system version as a raw string.', - }, - { - name: 'kernel', - level: 'extended', - type: 'keyword', - example: '4.4.0-112-generic', - description: 'Operating system kernel version as a raw string.', - }, - ], + 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: 'agent.hostname', - type: 'keyword', - description: 'Hostname of the agent.', - }, - ], - }, - { - key: 'beat', - title: 'Beat', - description: 'Contains common beat fields available in all event types.', - fields: [ - { - name: 'beat.timezone', - type: 'alias', - path: 'event.timezone', - migration: true, - }, - { - name: 'fields', - type: 'object', - object_type: 'keyword', - description: 'Contains user configurable fields.', - }, - { - name: 'error', - type: 'group', - description: 'Error fields containing additional info in case of errors.', - fields: [ { - name: 'type', + name: 'extension', + level: 'extended', type: 'keyword', - description: 'Error type.', + ignore_above: 1024, + description: 'File extension.', + example: 'png', }, - ], - }, - { - name: 'beat.name', - type: 'alias', - path: 'host.name', - migration: true, - }, - { - name: 'beat.hostname', - type: 'alias', - path: 'agent.hostname', - migration: true, - }, - ], - }, - { - key: 'cloud', - title: 'Cloud provider metadata', - description: 'Metadata from cloud providers added by the add_cloud_metadata processor.', - fields: [ - { - name: 'cloud.project.id', - example: 'project-x', - description: 'Name of the project in Google Cloud.', - }, - { - 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.', - short_config: false, - anchor: 'docker-processor', - fields: [ - { - name: 'docker', - type: 'group', - fields: [ { - name: 'container.id', - type: 'alias', - path: 'container.id', - migration: true, + name: 'gid', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Primary group ID (GID) of the file.', + example: '1001', }, { - name: 'container.image', - type: 'alias', - path: 'container.image.name', - migration: true, + name: 'group', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Primary group name of the file.', + example: 'alice', }, { - name: 'container.name', - type: 'alias', - path: 'container.name', - migration: true, + name: 'hash.md5', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'MD5 hash.', }, { - name: 'container.labels', - type: 'object', - object_type: 'keyword', - description: 'Image labels.', + name: 'hash.sha1', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'SHA1 hash.', }, - ], - }, - ], - }, - { - key: 'host', - title: 'Host', - description: 'Info collected for the host machine.', - anchor: 'host-processor', - }, - { - key: 'kubernetes', - title: 'Kubernetes', - description: 'Kubernetes metadata added by the kubernetes processor', - short_config: false, - anchor: 'kubernetes-processor', - fields: [ - { - name: 'kubernetes', - type: 'group', - fields: [ { - name: 'pod.name', + name: 'hash.sha256', + level: 'extended', type: 'keyword', - description: 'Kubernetes pod name', + ignore_above: 1024, + description: 'SHA256 hash.', }, { - name: 'pod.uid', + name: 'hash.sha512', + level: 'extended', type: 'keyword', - description: 'Kubernetes Pod UID', + ignore_above: 1024, + description: 'SHA512 hash.', }, { - name: 'namespace', + name: 'inode', + level: 'extended', type: 'keyword', - description: 'Kubernetes namespace', + ignore_above: 1024, + description: 'Inode representing the file in the filesystem.', + example: '256383', }, { - name: 'node.name', + name: 'mime_type', + level: 'extended', type: 'keyword', - description: 'Kubernetes node name', + 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: 'labels', - type: 'object', - description: 'Kubernetes labels map', + name: 'mode', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Mode of the file in octal representation.', + example: '0640', }, { - name: 'annotations', - type: 'object', - description: 'Kubernetes annotations map', + name: 'mtime', + level: 'extended', + type: 'date', + description: 'Last time the file content was modified.', }, { - name: 'container.name', + name: 'name', + level: 'extended', type: 'keyword', - description: 'Kubernetes container name', + ignore_above: 1024, + description: 'Name of the file including the extension, without the directory.', + example: 'example.png', }, { - name: 'container.image', + name: 'owner', + level: 'extended', type: 'keyword', - description: 'Kubernetes container image', + ignore_above: 1024, + description: "File owner's username.", + example: 'alice', }, - ], - }, - ], - }, - { - key: 'process', - title: 'Process', - description: 'Process metadata fields', - fields: [ - { - name: 'process', - type: 'group', - fields: [ - { - name: 'exe', - type: 'alias', - path: 'process.executable', - migration: true, - }, - ], - }, - ], - }, - { - key: 'common', - title: 'Common', - description: - 'These fields contain data about the environment in which the transaction or flow was captured.', - fields: [ - { - name: 'type', - description: - 'The type of the transaction (for example, HTTP, MySQL, Redis, or RUM) or "flow" in case of flows.', - required: true, - }, - { - name: 'server.process.name', - description: 'The name of the process that served the transaction.', - }, - { - name: 'server.process.args', - description: 'The command-line of the process that served the transaction.', - }, - { - name: 'server.process.executable', - description: 'Absolute path to the server process executable.', - }, - { - name: 'server.process.working_directory', - description: 'The working directory of the server process.', - }, - { - name: 'server.process.start', - description: 'The time the server process started.', - }, - { - name: 'client.process.name', - description: 'The name of the process that initiated the transaction.', - }, - { - name: 'client.process.args', - description: 'The command-line of the process that initiated the transaction.', - }, - { - name: 'client.process.executable', - description: 'Absolute path to the client process executable.', - }, - { - name: 'client.process.working_directory', - description: 'The working directory of the client process.', - }, - { - name: 'client.process.start', - description: 'The time the client process started.', - }, - { - 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`. Unless this field is disabled, it always has a value, and it matches the `client_ip` for non proxy clients.', - }, - { - name: 'transport', - type: 'alias', - path: 'network.transport', - migration: true, - description: - 'The transport protocol used for the transaction. If not specified, then tcp is assumed.', - }, - ], - }, - { - key: 'flows_event', - title: 'Flow Event', - description: 'These fields contain data about the flow itself.', - 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.', - }, - { - name: 'flow.id', - description: 'Internal flow ID based on connection meta data and address.', - }, - { - 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. ", - }, - { - 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.', - 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.', - 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).', - }, - { - 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.', - }, - { - 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.', - }, - { - 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`.', - }, - { - 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.', - }, - { - 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.', - }, - ], - }, - { - 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.', - }, - { - 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.', - }, - ], - }, - { - key: 'trans_measurements', - title: 'Measurements (Transactions)', - description: 'These fields contain measurements related to the transaction.', - 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.', - }, - { - 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.', - }, - ], - }, - { - 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', - example: 404, + 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: 'reply-text', + name: 'pe.company', + level: 'extended', type: 'keyword', - description: 'Text explaining the error.', + ignore_above: 1024, + description: 'Internal company name of the file, provided at compile-time.', + example: 'Microsoft Corporation', + default_field: false, }, { - name: 'class-id', - type: 'long', - description: 'Failing method class.', + 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: 'method-id', - type: 'long', - description: 'Failing method ID.', + 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: 'exchange', + name: 'pe.original_file_name', + level: 'extended', type: 'keyword', - description: 'Name of the exchange.', + ignore_above: 1024, + description: 'Internal name of the file, provided at compile-time.', + example: 'MSPAINT.EXE', + default_field: false, }, { - name: 'exchange-type', + name: 'pe.product', + level: 'extended', type: 'keyword', - description: 'Exchange type.', - example: 'fanout', + ignore_above: 1024, + description: 'Internal product name of the file, provided at compile-time.', + example: 'Microsoft® Windows® Operating System', + default_field: false, }, { - name: 'passive', - type: 'boolean', - description: 'If set, do not create exchange/queue.', + name: 'size', + level: 'extended', + type: 'long', + description: 'File size in bytes.\n\nOnly relevant when `file.type` is "file".', + example: 16384, }, { - name: 'durable', - type: 'boolean', - description: 'If set, request a durable exchange/queue.', + 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: 'exclusive', - type: 'boolean', - description: 'If set, request an exclusive queue.', + name: 'type', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'File type (file, dir, or symlink).', + example: 'file', }, { - name: 'auto-delete', - type: 'boolean', - description: 'If set, auto-delete queue when unused.', + 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: 'no-wait', - type: 'boolean', - description: 'If set, the server will not respond to the method.', + name: 'city_name', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'City name.', + example: 'Montreal', }, { - name: 'consumer-tag', - description: 'Identifier for the consumer, valid within the current channel.', + name: 'continent_name', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Name of the continent.', + example: 'North America', }, { - name: 'delivery-tag', - type: 'long', - description: 'The server-assigned and channel-specific delivery tag.', + name: 'country_iso_code', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Country ISO code.', + example: 'CA', }, { - name: 'message-count', - type: 'long', - description: - 'The number of messages in the queue, which will be zero for newly-declared queues.', + name: 'country_name', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Country name.', + example: 'Canada', }, { - name: 'consumer-count', - type: 'long', - description: 'The number of consumers of a queue.', + name: 'location', + level: 'core', + type: 'geo_point', + description: 'Longitude and latitude.', + example: '{ "lon": -73.614830, "lat": 45.505918 }', }, { - name: 'routing-key', + name: 'name', + level: 'extended', type: 'keyword', - description: 'Message routing key.', + 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: 'no-ack', - type: 'boolean', - description: 'If set, the server does not expect acknowledgements for messages.', + name: 'region_iso_code', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Region ISO code.', + example: 'CA-QC', }, { - name: 'no-local', - type: 'boolean', + 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: - 'If set, the server will not send messages to the connection that published them.', + 'Name of the directory the group is a member of.\n\nFor example, an LDAP or Active Directory domain name.', }, { - name: 'if-unused', - type: 'boolean', - description: 'Delete only if unused.', + name: 'id', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'Unique identifier for the group on the system/platform.', }, { - name: 'if-empty', - type: 'boolean', - description: 'Delete only if empty.', + 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: 'queue', + name: 'md5', + level: 'extended', type: 'keyword', - description: 'The queue name identifies the queue within the vhost.', + ignore_above: 1024, + description: 'MD5 hash.', }, { - name: 'redelivered', - type: 'boolean', - description: - 'Indicates that the message has been previously delivered to this or another client.', + name: 'sha1', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'SHA1 hash.', }, { - name: 'multiple', - type: 'boolean', - description: 'Acknowledge multiple messages.', + name: 'sha256', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'SHA256 hash.', }, { - name: 'arguments', - type: 'object', - description: - 'Optional additional arguments passed to some methods. Can be of various types.', + 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: 'mandatory', - type: 'boolean', - description: 'Indicates mandatory routing.', + name: 'architecture', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Operating system architecture.', + example: 'x86_64', }, { - name: 'immediate', - type: 'boolean', - description: 'Request immediate delivery.', + 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: 'content-type', + name: 'geo.city_name', + level: 'core', type: 'keyword', - description: 'MIME content type.', - example: 'text/plain', + ignore_above: 1024, + description: 'City name.', + example: 'Montreal', }, { - name: 'content-encoding', + name: 'geo.continent_name', + level: 'core', type: 'keyword', - description: 'MIME content encoding.', + ignore_above: 1024, + description: 'Name of the continent.', + example: 'North America', }, { - name: 'headers', - type: 'object', - object_type: 'keyword', - description: 'Message header field table.', + name: 'geo.country_iso_code', + level: 'core', + type: 'keyword', + ignore_above: 1024, + description: 'Country ISO code.', + example: 'CA', }, { - name: 'delivery-mode', + name: 'geo.country_name', + level: 'core', type: 'keyword', - description: 'Non-persistent (1) or persistent (2).', + ignore_above: 1024, + description: 'Country name.', + example: 'Canada', }, { - name: 'priority', - type: 'long', - description: 'Message priority, 0 to 9.', + name: 'geo.location', + level: 'core', + type: 'geo_point', + description: 'Longitude and latitude.', + example: '{ "lon": -73.614830, "lat": 45.505918 }', }, { - name: 'correlation-id', + name: 'geo.name', + level: 'extended', type: 'keyword', - description: 'Application correlation identifier.', + 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: 'reply-to', + name: 'geo.region_iso_code', + level: 'core', type: 'keyword', - description: 'Address to reply to.', + ignore_above: 1024, + description: 'Region ISO code.', + example: 'CA-QC', }, { - name: 'expiration', + name: 'geo.region_name', + level: 'core', type: 'keyword', - description: 'Message expiration specification.', + ignore_above: 1024, + description: 'Region name.', + example: 'Quebec', }, { - name: 'message-id', + name: 'hostname', + level: 'core', type: 'keyword', - description: 'Application message identifier.', + ignore_above: 1024, + description: + 'Hostname of the host.\n\nIt normally contains what the `hostname` command returns on the host machine.', }, { - name: 'timestamp', + name: 'id', + level: 'core', type: 'keyword', - description: 'Message timestamp.', + 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: 'type', - type: 'keyword', - description: 'Message type name.', + name: 'ip', + level: 'core', + type: 'ip', + description: 'Host ip addresses.', }, { - name: 'user-id', + name: 'mac', + level: 'core', type: 'keyword', - description: 'Creating user id.', + ignore_above: 1024, + description: 'Host mac addresses.', }, { - name: 'app-id', + name: 'name', + level: 'core', type: 'keyword', - description: 'Creating application id.', + 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.', }, - ], - }, - ], - }, - { - 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.', + name: 'os.family', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + description: 'OS family (such as redhat, debian, freebsd, windows).', + example: 'debian', }, { - name: 'request', - type: 'group', - description: 'Cassandra request.', - fields: [ + name: 'os.full', + level: 'extended', + type: 'keyword', + ignore_above: 1024, + multi_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: '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: 'query', - type: 'keyword', - description: 'The CQL query which client send to cassandra.', + name: 'text', + type: 'text', + norms: false, + default_field: false, }, ], + description: 'Operating system name, without the version.', + example: 'Mac OS X', }, { - name: 'response', + 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: [ @@ -3243,19 +6791,19 @@ export const packetbeatSchema: Schema = [ name: 'transaction_id', type: 'keyword', 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.', + '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 renewal process.', + '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 its reply -- either unicast or broadcast.', + 'Flags are set by the client to indicate how the DHCP server should\nits reply -- either unicast or broadcast.\n', }, { name: 'client_ip', @@ -3266,19 +6814,19 @@ export const packetbeatSchema: Schema = [ name: 'assigned_ip', type: 'ip', description: - 'The IP address that the DHCP server is assigning to the client. This field is also known as "your" IP address.', + '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 next step in the bootstrap process.', + '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 (i.e. a DHCP relay server).', + 'The relay IP address used by the client to contact the server\n(i.e. a DHCP relay server).\n', }, { name: 'client_mac', @@ -3289,13 +6837,13 @@ export const packetbeatSchema: Schema = [ name: 'server_name', type: 'keyword', description: - 'The name of the server sending the message. Optional. Used in DHCPOFFER or DHCPACK messages.', + '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).', + description: 'The message op code (bootrequest or bootreply).\n', }, { name: 'hops', @@ -3306,7 +6854,7 @@ export const packetbeatSchema: Schema = [ name: 'hardware_type', type: 'keyword', description: - 'The type of hardware used for the local network (Ethernet, LocalTalk, etc).', + 'The type of hardware used for the local network (Ethernet,\nLocalTalk, etc).\n', }, { name: 'option', @@ -3317,124 +6865,126 @@ export const packetbeatSchema: Schema = [ type: 'keyword', example: 'ack', description: - 'The specific type of DHCP message being sent (e.g. discover, offer, request, decline, ack, nak, release, inform).', + '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 specified configuration parameters.', + '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 the client to request that a particular IP address be assigned.', + '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 message.', + 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 client's subnet. ", + "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 client is willing to accept.', + '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 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. ", + "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 when resolving hostnames via the Domain Name System.', + '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 System servers available to the client.', + '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 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.', + '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 network.', + 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 subnet in seconds from Coordinated Universal Time (UTC). ", + "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 on the client's subnet. ", + "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 available to the client.', + '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 servers available to the client.', + '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.', + 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 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.', + '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 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.', + '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 until the client transitions to the RENEWING state.', + '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 until the client transitions to the REBINDING state.', + '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 in the DHCP header has been used for DHCP options. ", + "This option is used to identify a bootfile when the 'file' field\nin the DHCP header has been used for DHCP options.\n", }, ], }, @@ -3451,129 +7001,64 @@ export const packetbeatSchema: Schema = [ name: 'dns', type: 'group', fields: [ - { - name: 'id', - type: 'long', - description: - 'The DNS packet identifier assigned by the program that generated the query. The identifier is copied to the response.', - }, - { - name: 'op_code', - 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: 'flags.authoritative', type: 'boolean', description: - 'A DNS flag specifying that the responding server is an authority for the domain name used in the question.', + '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.', + '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.', + '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.', + '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.', + '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.', - }, - { - name: 'response_code', - description: 'The DNS status code.', - example: 'NOERROR', - }, - { - name: 'question.name', - description: - 'The domain name being queried. If the name field contains non-printable characters (below 32 or above 126), then those characters are represented as escaped base 10 integers (\\DDD). Back slashes and quotes are escaped. Tabs, carriage returns, and line feeds are converted to \\t, \\r, and respectively.', - example: 'www.google.com.', - }, - { - name: 'question.type', - description: 'The type of records being queried.', - example: 'AAAA', - }, - { - name: 'question.class', - description: 'The class of of records being queried.', - example: 'IN', + '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. 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.', + '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', - type: 'object', - description: - 'An array containing a dictionary about each answer section returned by the server.', - }, { name: 'answers_count', type: 'long', - description: 'The number of resource records contained in the `dns.answers` field.', - }, - { - name: 'answers.name', - description: 'The domain name to which this resource record pertains.', - example: 'example.com.', - }, - { - name: 'answers.type', - description: 'The type of data contained in this resource record.', - example: 'MX', - }, - { - name: 'answers.class', - description: 'The class of DNS data contained in this resource record.', - example: 'IN', - }, - { - name: 'answers.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.', - type: 'long', - }, - { - name: 'answers.data', - description: - 'The data describing the resource. The meaning of this data depends on the type and class of the resource record.', + 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.', + '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.', + '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', @@ -3594,13 +7079,13 @@ export const packetbeatSchema: Schema = [ name: 'additionals', type: 'object', description: - 'An array containing a dictionary for each additional section from the answer.', + '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.', + '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', @@ -3620,13 +7105,13 @@ export const packetbeatSchema: Schema = [ { 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.', + '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.', + 'The data describing the resource. The meaning of this data depends on the type and class of the resource record.\n', }, { name: 'opt.version', @@ -3672,7 +7157,7 @@ export const packetbeatSchema: Schema = [ 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.', + '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', @@ -3697,7 +7182,7 @@ export const packetbeatSchema: Schema = [ 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.', + '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', @@ -3720,7 +7205,7 @@ export const packetbeatSchema: Schema = [ { key: 'icmp', title: 'ICMP', - description: 'ICMP specific event fields.', + description: 'ICMP specific event fields.\n', fields: [ { name: 'icmp', @@ -3778,232 +7263,232 @@ export const packetbeatSchema: Schema = [ 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.', + '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.', + 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.', + '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.', + '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".', + '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).', + '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).', + '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.', + description: 'The binary protocol message opcode name.\n', }, { name: 'response.opcode', type: 'keyword', - description: 'The binary protocol message opcode name.', + description: 'The binary protocol message opcode name.\n', }, { name: 'request.opcode_value', type: 'long', - description: 'The binary protocol message opcode value.', + description: 'The binary protocol message opcode value.\n', }, { name: 'response.opcode_value', type: 'long', - description: 'The binary protocol message opcode value.', + 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.', + '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.', + '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.', + 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).', + '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).', + 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.', + 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).', + 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.', + '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.', + '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).', + 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).', + 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.', + 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.', + description: 'The byte count of the values being transferred.\n', }, { name: 'request.delta', type: 'long', - description: 'The counter increment/decrement delta value.', + description: 'The counter increment/decrement delta value.\n', }, { name: 'request.initial', type: 'long', description: - 'The counter increment/decrement initial value parameter (binary protocol only).', + 'The counter increment/decrement initial value parameter (binary protocol only).\n', }, { name: 'request.verbosity', type: 'long', - description: 'The value of the memcache "verbosity" command.', + 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.', + '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. ", + 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. ", + 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.', + '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).', + 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).', + 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).', + '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. ", + 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.', + 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.', + '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.', + '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.', + 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).', + '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".', + '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.', + description: 'The returned memcache version string.\n', }, ], }, @@ -4013,7 +7498,7 @@ export const packetbeatSchema: Schema = [ 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.', + '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', @@ -4022,57 +7507,57 @@ export const packetbeatSchema: Schema = [ { name: 'error', description: - 'If the MongoDB request has resulted in an error, this field contains the error message returned by the server.', + '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.', + '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.', + '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.', + description: 'The requested maximum number of documents to be returned.\n', }, { name: 'numberReturned', type: 'long', - description: 'The number of documents in the reply.', + description: 'The number of documents in the reply.\n', }, { name: 'startingFrom', - description: 'Where in the cursor this reply is starting.', + 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.', + '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.', + '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.', + '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.', + '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.', + 'The cursor identifier returned in the OP_REPLY. This must be the value that was returned from the database.\n', }, ], }, @@ -4081,7 +7566,7 @@ export const packetbeatSchema: Schema = [ { key: 'mysql', title: 'MySQL', - description: 'MySQL-specific event fields.', + description: 'MySQL-specific event fields.\n', fields: [ { name: 'mysql', @@ -4091,35 +7576,35 @@ export const packetbeatSchema: Schema = [ name: 'affected_rows', type: 'long', description: - 'If the MySQL command is successful, this field contains the affected number of rows of the last statement.', + '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.', + '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.', + '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.', + '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. ", + 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.', + description: 'The error code returned by MySQL.\n', }, { name: 'error_message', - description: 'The error info message returned by MySQL.', + description: 'The error info message returned by MySQL.\n', }, ], }, @@ -4150,7 +7635,7 @@ export const packetbeatSchema: Schema = [ }, { name: 'opcode', - description: 'NFS operation name, or main operation name, in case of COMPOUND calls.', + description: 'NFS operation name, or main operation name, in case of COMPOUND calls.\n', }, { name: 'status', @@ -4219,7 +7704,7 @@ export const packetbeatSchema: Schema = [ { key: 'pgsql', title: 'PostgreSQL', - description: 'PostgreSQL-specific event fields.', + description: 'PostgreSQL-specific event fields.\n', fields: [ { name: 'pgsql', @@ -4242,12 +7727,12 @@ export const packetbeatSchema: Schema = [ { name: 'num_fields', description: - 'If the SELECT query if successful, this field is set to the number of fields returned.', + '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.', + 'If the SELECT query if successful, this field is set to the number of rows returned.\n', }, ], }, @@ -4256,7 +7741,7 @@ export const packetbeatSchema: Schema = [ { key: 'redis', title: 'Redis', - description: 'Redis-specific event fields.', + description: 'Redis-specific event fields.\n', fields: [ { name: 'redis', @@ -4264,12 +7749,12 @@ export const packetbeatSchema: Schema = [ fields: [ { name: 'return_value', - description: 'The return value of the Redis command in a human readable format.', + 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.', + 'If the Redis command has resulted in an error, this field contains the error message returned by the Redis server.\n', }, ], }, @@ -4278,7 +7763,7 @@ export const packetbeatSchema: Schema = [ { key: 'thrift', title: 'Thrift-RPC', - description: 'Thrift-RPC specific event fields.', + description: 'Thrift-RPC specific event fields.\n', fields: [ { name: 'thrift', @@ -4287,521 +7772,785 @@ export const packetbeatSchema: Schema = [ { 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.', + '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.', + 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.', + '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.', + 'If the call resulted in exceptions, this field contains the exceptions in a human readable format.\n', }, ], }, ], - }, - { - key: 'tls', - title: 'TLS', - description: 'TLS-specific event fields.', - fields: [ - { - name: 'tls', - type: 'group', - fields: [ - { - name: 'version', - type: 'keyword', - description: 'The version of the TLS protocol used.', - example: 'TLS 1.3', - }, - { - name: 'handshake_completed', - type: 'boolean', - description: - 'Whether the TLS negotiation has been successful and the session has transitioned to encrypted mode.', - }, - { - name: 'resumed', - type: 'boolean', - description: 'If the TLS session has been resumed from a previous session.', - }, - { - 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.', - }, - { - name: 'client_certificate_requested', - type: 'boolean', - description: - 'Whether the server has requested the client to authenticate itself using a client certificate.', - }, - { - 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.', - }, - { - name: 'supported_ciphers', - type: 'array', - description: - 'List of ciphers the client is willing to use for this session. See https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4', - }, - { - name: 'supported_compression_methods', - type: 'array', - description: - 'The list of compression methods the client supports. See https://www.iana.org/assignments/comp-meth-ids/comp-meth-ids.xhtml', - }, - { - 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.', - }, - { - name: 'session_ticket', - type: 'keyword', - description: - 'Length of the session ticket, if provided, or an empty string to advertise support for tickets.', - }, - { - name: 'supported_versions', - type: 'keyword', - description: 'List of TLS versions that the client is willing to use.', - }, - { - name: 'supported_groups', - type: 'keyword', - description: - 'List of Elliptic Curve Cryptography (ECC) curve groups supported by the client.', - }, - { - name: 'signature_algorithms', - type: 'keyword', - description: - 'List of signature algorithms that may be use in digital signatures.', - }, - { - 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.', - }, - { - name: '_unparsed_', - type: 'keyword', - description: 'List of extensions that were left unparsed by Packetbeat.', - }, - ], - }, - ], - }, + }, + { + key: 'tls_detailed', + title: 'Detailed TLS', + description: 'Detailed TLS-specific event fields.\n', + fields: [ + { + name: 'tls', + type: 'group', + fields: [ { - name: 'server_hello', + name: 'detailed', type: 'group', + default_fields: false, 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.', - }, - { - name: 'selected_cipher', - type: 'keyword', - description: - 'The cipher suite selected by the server from the list provided by in the client hello.', + description: 'The version of the TLS protocol used.\n', + example: 'TLS 1.3', }, { - name: 'selected_compression_method', + name: 'resumption_method', type: 'keyword', description: - 'The compression method selected by the server from the list provided in the client hello.', + '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: 'session_id', - type: 'keyword', + name: 'client_certificate_requested', + type: 'boolean', description: - 'Unique number to identify the session for the corresponding connection with the client.', + 'Whether the server has requested the client to authenticate itself using a client certificate.\n', }, { - name: 'extensions', + name: 'client_hello', type: 'group', - description: 'The hello extensions provided by the server.', fields: [ { - name: 'application_layer_protocol_negotiation', - type: 'array', - description: 'Negotiated application layer protocol', - }, - { - name: 'session_ticket', + name: 'version', type: 'keyword', description: - 'Used to announce that a session ticket will be provided by the server. Always an empty string.', + 'The version of the TLS protocol by which the client wishes to communicate during this session.\n', }, { - name: 'supported_versions', + name: 'session_id', type: 'keyword', - description: 'Negotiated TLS version to be used.', + description: + 'Unique number to identify the session for the corresponding connection with the client.\n', }, { - name: 'ec_points_formats', + name: 'supported_compression_methods', type: 'keyword', description: - 'List of Elliptic Curve (EC) point formats. Indicates the set of point formats that the server can parse.', + 'The list of compression methods the client supports. See https://www.iana.org/assignments/comp-meth-ids/comp-meth-ids.xhtml\n', }, { - name: '_unparsed_', - type: 'keyword', - description: 'List of extensions that were left unparsed by Packetbeat.', + 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: '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. ", - }, - { - 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. ", - }, - { - name: 'alternative_names', - type: 'array', - description: 'Subject Alternative Names for this certificate.', - }, - { - name: 'raw', - type: 'keyword', - description: 'The raw certificate in PEM format.', - }, - { - name: 'subject', + name: 'server_hello', type: 'group', - description: 'Subject represented by this certificate.', fields: [ { - name: 'country', - type: 'keyword', - description: 'Country code.', - }, - { - name: 'organization', + name: 'version', type: 'keyword', - description: 'Organization name.', + 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: 'organizational_unit', + name: 'selected_compression_method', type: 'keyword', - description: 'Unit within organization.', + description: + 'The compression method selected by the server from the list provided in the client hello.\n', }, { - name: 'province', + name: 'session_id', type: 'keyword', - description: 'Province or region within country.', + description: + 'Unique number to identify the session for the corresponding connection with the client.\n', }, { - name: 'common_name', - type: 'keyword', - description: 'Name or host name identified by the certificate.', + 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: 'issuer', + name: 'client_certificate', type: 'group', - description: 'Entity that issued and signed this certificate.', + description: 'Certificate provided by the client for authentication.', fields: [ { - name: 'country', - type: 'keyword', - description: 'Country code.', + name: 'version', + type: 'long', + description: 'X509 format version.', }, { - name: 'organization', + name: 'serial_number', type: 'keyword', - description: 'Organization name.', + description: "The certificate's serial number.", }, { - name: 'organizational_unit', - type: 'keyword', - description: 'Unit within organization.', + name: 'not_before', + type: 'date', + description: 'Date before which the certificate is not valid.', }, { - name: 'province', - type: 'keyword', - description: 'Province or region within country.', + name: 'not_after', + type: 'date', + description: 'Date after which the certificate expires.', }, { - name: 'common_name', + name: 'public_key_algorithm', type: 'keyword', - description: 'Name or host name identified by the certificate.', + description: + "The algorithm used for this certificate's public key. One of RSA, DSA or ECDSA.\n", }, - ], - }, - { - name: 'fingerprint', - type: 'group', - fields: [ { - name: 'md5', - type: 'keyword', - description: "Certificate's MD5 fingerprint.", + name: 'public_key_size', + type: 'long', + description: 'Size of the public key.', }, { - name: 'sha1', + name: 'signature_algorithm', type: 'keyword', - description: "Certificate's SHA-1 fingerprint.", + description: "The algorithm used for the certificate's signature.\n", }, { - name: 'sha256', + name: 'alternative_names', type: 'keyword', - description: "Certificate's SHA-256 fingerprint.", + 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. ", - }, - { - 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. ", - }, - { - name: 'alternative_names', - type: 'array', - description: 'Subject Alternative Names for this certificate.', - }, - { - name: 'raw', - type: 'keyword', - description: 'The raw certificate in PEM format.', - }, - { - name: 'subject', + name: 'server_certificate', type: 'group', - description: 'Subject represented by this certificate.', + description: 'Certificate provided by the server for authentication.', fields: [ { - name: 'country', - type: 'keyword', - description: 'Country code.', + name: 'version', + type: 'long', + description: 'X509 format version.', }, { - name: 'organization', + name: 'serial_number', type: 'keyword', - description: 'Organization name.', + 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: 'organizational_unit', + name: 'public_key_algorithm', type: 'keyword', - description: 'Unit within organization.', + 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: 'province', + name: 'signature_algorithm', type: 'keyword', - description: 'Province or region within country.', + description: "The algorithm used for the certificate's signature.\n", }, { - name: 'common_name', + name: 'alternative_names', type: 'keyword', - description: 'Name or host name identified by the certificate.', + 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: '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: 'server_certificate_chain', + type: 'array', + description: 'Chain of trust for the server certificate.', }, { - name: 'fingerprint', - type: 'group', - fields: [ - { - name: 'md5', - type: 'keyword', - description: "Certificate's MD5 fingerprint.", - }, - { - name: 'sha1', - type: 'keyword', - description: "Certificate's SHA-1 fingerprint.", - }, - { - name: 'sha256', - type: 'keyword', - description: "Certificate's SHA-256 fingerprint.", - }, - ], + name: 'client_certificate_chain', + type: 'array', + description: 'Chain of trust for the client certificate.', }, - ], - }, - { - 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.', - }, - { - name: 'fingerprints', - type: 'group', - description: 'Fingerprints for this TLS session.', - fields: [ { - name: 'ja3', - type: 'group', - description: 'JA3 TLS client fingerprint', - fields: [ - { - name: 'hash', - type: 'keyword', - description: 'The JA3 fingerprint hash for the client side.', - }, - { - name: 'str', - type: 'keyword', - description: 'The JA3 string used to calculate the hash.', - }, - ], + 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/legacy/plugins/siem/server/utils/beat_schema/index.test.ts b/x-pack/legacy/plugins/siem/server/utils/beat_schema/index.test.ts index 53ed7c26b877f..56ceca2b70e9c 100644 --- a/x-pack/legacy/plugins/siem/server/utils/beat_schema/index.test.ts +++ b/x-pack/legacy/plugins/siem/server/utils/beat_schema/index.test.ts @@ -17,175 +17,94 @@ describe('Schema Beat', () => { 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. For log events this is the date/time when the event was generated, and not when it was read. Required field for all events.', + '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', }, - tags: { - description: 'List of keywords used to tag each event.', - example: '["production", "env2"]', - name: 'tags', - type: 'keyword', - }, labels: { description: - '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: '{"env":"production","application":"foo-bar"}', + '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. In other use cases the message field can be used to concatenate different values which are then freely searchable. If multiple messages exist, they can be combined into one message.', + '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 any, that collects, detects, or observes events on a host, or takes measurements on a host. Examples include Beats. Agents may also run on observers. ECS agent.* fields shall be populated with details of the agent running on the host or observer where the event happened or the measurement was taken.', + '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.version': { - description: 'Version of the agent.', - example: '6.0.0-rc2', - name: 'version', + '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: - '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.', + '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. 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.', + '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.id': { - description: - 'Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.', - example: '8a4f500d', - name: 'id', - type: 'keyword', - }, - 'agent.ephemeral_id': { - description: - 'Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but `agent.id` does not.', - example: '8a4f500f', - name: 'ephemeral_id', + 'agent.version': { + description: 'Version of the agent.', + example: '6.0.0-rc2', + name: 'version', type: 'keyword', }, }, }, - client: { + as: { description: - 'A client is defined as the initiator of a network connection for events regarding sessions, connections, or bidirectional flow records. For TCP events, the client is the initiator of the TCP connection that sends the SYN packet(s). For other protocols, the client is generally the initiator or requestor in the network transaction. Some systems use the term "originator" to refer the client in TCP connections. The client fields describe details about the system acting as the client in the network event. Client fields are usually populated in conjunction with server fields. Client fields are generally not populated for packet-level events. Client / server representations can add semantic context to an exchange, which is helpful to visualize the data in certain situations. If your context falls in that category, you should still ensure that source and destination are filled appropriately.', - name: 'client', + '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: { - 'client.address': { + 'as.number': { 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: 'address', - type: 'keyword', - }, - 'client.ip': { - description: - 'IP address of the client. Can be one or multiple IPv4 or IPv6 addresses.', - name: 'ip', - type: 'ip', - }, - 'client.port': { - description: 'Port of the client.', - name: 'port', + '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', }, - 'client.mac': { - description: 'MAC address of the client.', - name: 'mac', - type: 'keyword', - }, - 'client.domain': { - description: 'Client domain.', - name: 'domain', - type: 'keyword', - }, - 'client.bytes': { - description: 'Bytes sent from the client to the server.', - example: 184, - format: 'bytes', - name: 'bytes', - type: 'long', - }, - 'client.packets': { - description: 'Packets sent from the client to the server.', - example: 12, - name: 'packets', - type: 'long', - }, - 'client.geo': { - description: - 'Geo fields can carry data about a specific location related to an event or geo information derived from an IP field.', - name: 'geo', - type: 'group', - }, - 'client.geo.location': { - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - name: 'location', - type: 'geo_point', - }, - 'client.geo.continent_name': { - description: 'Name of the continent.', - example: 'North America', - name: 'continent_name', - type: 'keyword', - }, - 'client.geo.country_name': { - description: 'Country name.', - example: 'Canada', - name: 'country_name', - type: 'keyword', - }, - 'client.geo.region_name': { - description: 'Region name.', - example: 'Quebec', - name: 'region_name', - type: 'keyword', - }, - 'client.geo.city_name': { - description: 'City name.', - example: 'Montreal', - name: 'city_name', - type: 'keyword', - }, - 'client.geo.country_iso_code': { - description: 'Country ISO code.', - example: 'CA', - name: 'country_iso_code', - type: 'keyword', - }, - 'client.geo.region_iso_code': { - description: 'Region ISO code.', - example: 'CA-QC', - name: 'region_iso_code', - type: 'keyword', - }, - 'client.geo.name': { - 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: 'name', + 'as.organization.name': { + description: 'Organization name.', + example: 'Google LLC', + name: 'organization.name', type: 'keyword', }, }, @@ -198,175 +117,94 @@ describe('Schema Beat', () => { 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. For log events this is the date/time when the event was generated, and not when it was read. Required field for all events.', + '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', }, - tags: { - description: 'List of keywords used to tag each event.', - example: '["production", "env2"]', - name: 'tags', - type: 'keyword', - }, labels: { description: - '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: '{"env":"production","application":"foo-bar"}', + '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. In other use cases the message field can be used to concatenate different values which are then freely searchable. If multiple messages exist, they can be combined into one message.', + '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 any, that collects, detects, or observes events on a host, or takes measurements on a host. Examples include Beats. Agents may also run on observers. ECS agent.* fields shall be populated with details of the agent running on the host or observer where the event happened or the measurement was taken.', + '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.version': { - description: 'Version of the agent.', - example: '6.0.0-rc2', - name: 'version', + '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: - '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.', + '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. 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.', + '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.id': { - description: - 'Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.', - example: '8a4f500d', - name: 'id', - type: 'keyword', - }, - 'agent.ephemeral_id': { - description: - 'Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but `agent.id` does not.', - example: '8a4f500f', - name: 'ephemeral_id', + 'agent.version': { + description: 'Version of the agent.', + example: '6.0.0-rc2', + name: 'version', type: 'keyword', }, }, }, - client: { + as: { description: - 'A client is defined as the initiator of a network connection for events regarding sessions, connections, or bidirectional flow records. For TCP events, the client is the initiator of the TCP connection that sends the SYN packet(s). For other protocols, the client is generally the initiator or requestor in the network transaction. Some systems use the term "originator" to refer the client in TCP connections. The client fields describe details about the system acting as the client in the network event. Client fields are usually populated in conjunction with server fields. Client fields are generally not populated for packet-level events. Client / server representations can add semantic context to an exchange, which is helpful to visualize the data in certain situations. If your context falls in that category, you should still ensure that source and destination are filled appropriately.', - name: 'client', + '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: { - 'client.address': { + 'as.number': { 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: 'address', - type: 'keyword', - }, - 'client.ip': { - description: - 'IP address of the client. Can be one or multiple IPv4 or IPv6 addresses.', - name: 'ip', - type: 'ip', - }, - 'client.port': { - description: 'Port of the client.', - name: 'port', - type: 'long', - }, - 'client.mac': { - description: 'MAC address of the client.', - name: 'mac', - type: 'keyword', - }, - 'client.domain': { - description: 'Client domain.', - name: 'domain', - type: 'keyword', - }, - 'client.bytes': { - description: 'Bytes sent from the client to the server.', - example: 184, - format: 'bytes', - name: 'bytes', - type: 'long', - }, - 'client.packets': { - description: 'Packets sent from the client to the server.', - example: 12, - name: 'packets', + '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', }, - 'client.geo': { - description: - 'Geo fields can carry data about a specific location related to an event or geo information derived from an IP field.', - name: 'geo', - type: 'group', - }, - 'client.geo.location': { - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - name: 'location', - type: 'geo_point', - }, - 'client.geo.continent_name': { - description: 'Name of the continent.', - example: 'North America', - name: 'continent_name', - type: 'keyword', - }, - 'client.geo.country_name': { - description: 'Country name.', - example: 'Canada', - name: 'country_name', - type: 'keyword', - }, - 'client.geo.region_name': { - description: 'Region name.', - example: 'Quebec', - name: 'region_name', - type: 'keyword', - }, - 'client.geo.city_name': { - description: 'City name.', - example: 'Montreal', - name: 'city_name', - type: 'keyword', - }, - 'client.geo.country_iso_code': { - description: 'Country ISO code.', - example: 'CA', - name: 'country_iso_code', - type: 'keyword', - }, - 'client.geo.region_iso_code': { - description: 'Region ISO code.', - example: 'CA-QC', - name: 'region_iso_code', - type: 'keyword', - }, - 'client.geo.name': { - 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: 'name', + 'as.organization.name': { + description: 'Organization name.', + example: 'Google LLC', + name: 'organization.name', type: 'keyword', }, }, @@ -379,175 +217,94 @@ describe('Schema Beat', () => { 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. For log events this is the date/time when the event was generated, and not when it was read. Required field for all events.', + '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', }, - tags: { - description: 'List of keywords used to tag each event.', - example: '["production", "env2"]', - name: 'tags', - type: 'keyword', - }, labels: { description: - '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: '{"env":"production","application":"foo-bar"}', + '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. In other use cases the message field can be used to concatenate different values which are then freely searchable. If multiple messages exist, they can be combined into one message.', + '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 any, that collects, detects, or observes events on a host, or takes measurements on a host. Examples include Beats. Agents may also run on observers. ECS agent.* fields shall be populated with details of the agent running on the host or observer where the event happened or the measurement was taken.', + '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.version': { - description: 'Version of the agent.', - example: '6.0.0-rc2', - name: 'version', + '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: - '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.', + '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. 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.', + '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.id': { - description: - 'Unique identifier of this agent (if one exists). Example: For Beats this would be beat.id.', - example: '8a4f500d', - name: 'id', - type: 'keyword', - }, - 'agent.ephemeral_id': { - description: - 'Ephemeral identifier of this agent (if one exists). This id normally changes across restarts, but `agent.id` does not.', - example: '8a4f500f', - name: 'ephemeral_id', + 'agent.version': { + description: 'Version of the agent.', + example: '6.0.0-rc2', + name: 'version', type: 'keyword', }, }, }, - client: { + as: { description: - 'A client is defined as the initiator of a network connection for events regarding sessions, connections, or bidirectional flow records. For TCP events, the client is the initiator of the TCP connection that sends the SYN packet(s). For other protocols, the client is generally the initiator or requestor in the network transaction. Some systems use the term "originator" to refer the client in TCP connections. The client fields describe details about the system acting as the client in the network event. Client fields are usually populated in conjunction with server fields. Client fields are generally not populated for packet-level events. Client / server representations can add semantic context to an exchange, which is helpful to visualize the data in certain situations. If your context falls in that category, you should still ensure that source and destination are filled appropriately.', - name: 'client', + '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: { - 'client.address': { + 'as.number': { 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: 'address', - type: 'keyword', - }, - 'client.ip': { - description: - 'IP address of the client. Can be one or multiple IPv4 or IPv6 addresses.', - name: 'ip', - type: 'ip', - }, - 'client.port': { - description: 'Port of the client.', - name: 'port', - type: 'long', - }, - 'client.mac': { - description: 'MAC address of the client.', - name: 'mac', - type: 'keyword', - }, - 'client.domain': { - description: 'Client domain.', - name: 'domain', - type: 'keyword', - }, - 'client.bytes': { - description: 'Bytes sent from the client to the server.', - example: 184, - format: 'bytes', - name: 'bytes', + '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', }, - 'client.packets': { - description: 'Packets sent from the client to the server.', - example: 12, - name: 'packets', - type: 'long', - }, - 'client.geo': { - description: - 'Geo fields can carry data about a specific location related to an event or geo information derived from an IP field.', - name: 'geo', - type: 'group', - }, - 'client.geo.location': { - description: 'Longitude and latitude.', - example: '{ "lon": -73.614830, "lat": 45.505918 }', - name: 'location', - type: 'geo_point', - }, - 'client.geo.continent_name': { - description: 'Name of the continent.', - example: 'North America', - name: 'continent_name', - type: 'keyword', - }, - 'client.geo.country_name': { - description: 'Country name.', - example: 'Canada', - name: 'country_name', - type: 'keyword', - }, - 'client.geo.region_name': { - description: 'Region name.', - example: 'Quebec', - name: 'region_name', - type: 'keyword', - }, - 'client.geo.city_name': { - description: 'City name.', - example: 'Montreal', - name: 'city_name', - type: 'keyword', - }, - 'client.geo.country_iso_code': { - description: 'Country ISO code.', - example: 'CA', - name: 'country_iso_code', - type: 'keyword', - }, - 'client.geo.region_iso_code': { - description: 'Region ISO code.', - example: 'CA-QC', - name: 'region_iso_code', - type: 'keyword', - }, - 'client.geo.name': { - 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: 'name', + 'as.organization.name': { + description: 'Organization name.', + example: 'Google LLC', + name: 'organization.name', type: 'keyword', }, }, @@ -562,40 +319,58 @@ describe('Schema Beat', () => { '_id', '_index', '@timestamp', - 'tags', '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', @@ -605,55 +380,17 @@ describe('Schema Beat', () => { 'meta.cloud.region', 'docker', 'kubernetes', - 'type', - 'server.process.name', - 'server.process.args', - 'server.process.executable', - 'server.process.working_directory', - 'server.process.start', - 'client.process.name', - 'client.process.args', - 'client.process.executable', - 'client.process.working_directory', - 'client.process.start', - 'real_ip', - 'transport', - 'flow.final', - 'flow.id', - 'flow.vlan', - 'flow_id', - 'final', - 'vlan', - 'source.stats.net_bytes_total', - 'source.stats.net_packets_total', - 'dest.stats.net_bytes_total', - 'dest.stats.net_packets_total', - 'status', - 'method', - 'resource', - 'path', - 'query', - 'params', - 'notes', - 'request', - 'response', - 'bytes_in', - 'bytes_out', - 'amqp', - 'no_request', - 'cassandra', - 'dhcpv4', - 'dns', - 'icmp', - 'memcache', - 'mongodb', - 'mysql', - 'nfs', - 'rpc', - 'pgsql', - 'redis', - 'thrift', - 'tls', + '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/legacy/plugins/siem/server/utils/beat_schema/index.ts b/x-pack/legacy/plugins/siem/server/utils/beat_schema/index.ts index a191bd835a7c7..3e9be4e265e63 100644 --- a/x-pack/legacy/plugins/siem/server/utils/beat_schema/index.ts +++ b/x-pack/legacy/plugins/siem/server/utils/beat_schema/index.ts @@ -106,19 +106,14 @@ export const getIndexSchemaDoc = memoize((index: string) => { ...extraSchemaField, ...convertSchemaToAssociativeArray(winlogbeatSchema), }; - } else if (index.match('ecs') != null) { - return { - ...extraSchemaField, - ...ecsSchema, - }; } - return {}; + return { + ...extraSchemaField, + ...convertSchemaToAssociativeArray(ecsSchema), + }; }); export const hasDocumentation = (index: string, path: string): boolean => { - if (index === 'unknown') { - return false; - } const splitPath = path.split('.'); const category = splitPath.length > 0 ? splitPath[0] : null; if (category === null) { @@ -131,16 +126,13 @@ export const hasDocumentation = (index: string, path: string): boolean => { }; export const getDocumentation = (index: string, path: string) => { - if (index === 'unknown') { - return ''; - } const splitPath = path.split('.'); const category = splitPath.length > 0 ? splitPath[0] : null; if (category === null) { - return ''; + return {}; } if (splitPath.length > 1) { - return get([category, 'fields', path], getIndexSchemaDoc(index)) || ''; + return get([category, 'fields', path], getIndexSchemaDoc(index)) || {}; } - return get(category, getIndexSchemaDoc(index)) || ''; + return get(category, getIndexSchemaDoc(index)) || {}; }; diff --git a/x-pack/legacy/plugins/siem/server/utils/beat_schema/type.ts b/x-pack/legacy/plugins/siem/server/utils/beat_schema/type.ts index f34519da34ee8..2b7be8f4b7539 100644 --- a/x-pack/legacy/plugins/siem/server/utils/beat_schema/type.ts +++ b/x-pack/legacy/plugins/siem/server/utils/beat_schema/type.ts @@ -12,25 +12,35 @@ export type IndexAlias = 'auditbeat' | 'filebeat' | 'packetbeat' | 'ecs' | 'winl */ export interface SchemaFields { + default_field: boolean; + default_fields: boolean; definition: string; + deprecated: string; description: string; doc_values: boolean; - example: string | number | object; + 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>; @@ -48,33 +58,6 @@ export interface SchemaItem { export type Schema = Array>; -/* - * ECS Interface - * - */ - -interface EcsField { - description: string; - example: string; - footnote: string; - group: number; - level: string; - name: string; - required: boolean; - type: string; -} - -interface EcsNamespace { - description: string; - fields: Readonly>; - group: number; - name: string; - title: string; - type: string; -} - -export type EcsSchema = Readonly>; - /* * Associative Array Output Interface * diff --git a/x-pack/legacy/plugins/tilemap/index.js b/x-pack/legacy/plugins/tilemap/index.js index 767a0fe72985e..d4105519ee0a7 100644 --- a/x-pack/legacy/plugins/tilemap/index.js +++ b/x-pack/legacy/plugins/tilemap/index.js @@ -15,7 +15,7 @@ export const tilemap = kibana => { require: ['xpack_main', 'vis_type_vislib'], publicDir: resolve(__dirname, 'public'), uiExports: { - visTypeEnhancers: ['plugins/tilemap/vis_type_enhancers/update_tilemap_settings'], + hacks: ['plugins/tilemap/vis_type_enhancers/update_tilemap_settings'], }, init: function(server) { const thisPlugin = this; diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/alerts/uptime_alerts_flyout_wrapper.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/alerts/uptime_alerts_flyout_wrapper.tsx index b547f8b076f93..a49468ad3dd06 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/alerts/uptime_alerts_flyout_wrapper.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/connected/alerts/uptime_alerts_flyout_wrapper.tsx @@ -17,7 +17,7 @@ interface Props { export const UptimeAlertsFlyoutWrapper = ({ alertTypeId, canChangeTrigger }: Props) => { const dispatch = useDispatch(); - const setAddFlyoutVisiblity = (value: React.SetStateAction) => + const setAddFlyoutVisibility = (value: React.SetStateAction) => // @ts-ignore the value here is a boolean, and it works with the action creator function dispatch(setAlertFlyoutVisible(value)); @@ -28,7 +28,7 @@ export const UptimeAlertsFlyoutWrapper = ({ alertTypeId, canChangeTrigger }: Pro alertFlyoutVisible={alertFlyoutVisible} alertTypeId={alertTypeId} canChangeTrigger={canChangeTrigger} - setAlertFlyoutVisibility={setAddFlyoutVisiblity} + setAlertFlyoutVisibility={setAddFlyoutVisibility} /> ); }; diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/alerts/alert_monitor_status.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/alerts/alert_monitor_status.tsx index 5143e1c963904..b86e85f35b17d 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/alerts/alert_monitor_status.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/alerts/alert_monitor_status.tsx @@ -268,7 +268,21 @@ export const AlertMonitorStatusComponent: React.FC = pr /> } data-test-subj="xpack.uptime.alerts.monitorStatus.numTimesExpression" - description="any monitor is down >" + description={ + filters + ? i18n.translate( + 'xpack.uptime.alerts.monitorStatus.numTimesExpression.matchingMonitors.description', + { + defaultMessage: 'matching monitors are down >', + } + ) + : i18n.translate( + 'xpack.uptime.alerts.monitorStatus.numTimesExpression.anyMonitors.description', + { + defaultMessage: 'any monitor is down >', + } + ) + } id="ping-count" value={`${numTimes} times`} /> diff --git a/x-pack/plugins/actions/README.md b/x-pack/plugins/actions/README.md index d217d26e84836..82cc09f5e9eca 100644 --- a/x-pack/plugins/actions/README.md +++ b/x-pack/plugins/actions/README.md @@ -28,7 +28,7 @@ Table of Contents - [RESTful API](#restful-api) - [`POST /api/action`: Create action](#post-apiaction-create-action) - [`DELETE /api/action/{id}`: Delete action](#delete-apiactionid-delete-action) - - [`GET /api/action/_find`: Find actions](#get-apiactionfind-find-actions) + - [`GET /api/action/_getAll`: Get all actions](#get-apiaction-get-all-actions) - [`GET /api/action/{id}`: Get action](#get-apiactionid-get-action) - [`GET /api/action/types`: List action types](#get-apiactiontypes-list-action-types) - [`PUT /api/action/{id}`: Update action](#put-apiactionid-update-action) @@ -92,6 +92,7 @@ Built-In-Actions are configured using the _xpack.actions_ namespoace under _kiba | _xpack.actions._**enabled** | Feature toggle which enabled Actions in Kibana. | boolean | | _xpack.actions._**whitelistedHosts** | Which _hostnames_ are whitelisted for the Built-In-Action? This list should contain hostnames of every external service you wish to interact with using Webhooks, Email or any other built in Action. Note that you may use the string "\*" in place of a specific hostname to enable Kibana to target any URL, but keep in mind the potential use of such a feature to execute [SSRF](https://www.owasp.org/index.php/Server_Side_Request_Forgery) attacks from your server. | Array | | _xpack.actions._**enabledActionTypes** | A list of _actionTypes_ id's that are enabled. A "\*" may be used as an element to indicate all registered actionTypes should be enabled. The actionTypes registered for Kibana are `.server-log`, `.slack`, `.email`, `.index`, `.pagerduty`, `.webhook`. Default: `["*"]` | Array | +| _xpack.actions._**preconfigured** | A list of preconfigured actions. Default: `[]` | Array | #### Whitelisting Built-in Action Types @@ -174,11 +175,13 @@ Params: | -------- | --------------------------------------------- | ------ | | id | The id of the action you're trying to delete. | string | -### `GET /api/action/_find`: Find actions +### `GET /api/action/_getAll`: Get all actions -Params: +No parameters. -See the [saved objects API documentation for find](https://www.elastic.co/guide/en/kibana/master/saved-objects-api-find.html). All the properties are the same except that you cannot pass in `type`. +Return all actions from saved objects merged with predefined list. +Use the [saved objects API for find](https://www.elastic.co/guide/en/kibana/master/saved-objects-api-find.html) with the proprties: `type: 'action'` and `perPage: 10000`. +List of predefined actions should be set up in Kibana.yaml. ### `GET /api/action/{id}`: Get action diff --git a/x-pack/plugins/actions/common/types.ts b/x-pack/plugins/actions/common/types.ts index f3042a701211f..61b338d47b9f5 100644 --- a/x-pack/plugins/actions/common/types.ts +++ b/x-pack/plugins/actions/common/types.ts @@ -20,4 +20,5 @@ export interface ActionResult { actionTypeId: string; name: string; config: Record; + isPreconfigured: boolean; } diff --git a/x-pack/plugins/actions/server/actions_client.mock.ts b/x-pack/plugins/actions/server/actions_client.mock.ts index 8a39d68f40bb6..431bfb1e99c3b 100644 --- a/x-pack/plugins/actions/server/actions_client.mock.ts +++ b/x-pack/plugins/actions/server/actions_client.mock.ts @@ -12,9 +12,9 @@ const createActionsClientMock = () => { const mocked: jest.Mocked = { create: jest.fn(), get: jest.fn(), - find: jest.fn(), delete: jest.fn(), update: jest.fn(), + getAll: jest.fn(), }; return mocked; }; diff --git a/x-pack/plugins/actions/server/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client.test.ts index 0df07ad58fb9e..955e1569380a5 100644 --- a/x-pack/plugins/actions/server/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client.test.ts @@ -51,6 +51,7 @@ beforeEach(() => { savedObjectsClient, scopedClusterClient, defaultKibanaIndex, + preconfiguredActions: [], }); }); @@ -83,6 +84,7 @@ describe('create()', () => { }); expect(result).toEqual({ id: '1', + isPreconfigured: false, name: 'my name', actionTypeId: 'my-action-type', config: {}, @@ -178,6 +180,7 @@ describe('create()', () => { }); expect(result).toEqual({ id: '1', + isPreconfigured: false, name: 'my name', actionTypeId: 'my-action-type', config: { @@ -226,6 +229,7 @@ describe('create()', () => { savedObjectsClient, scopedClusterClient, defaultKibanaIndex, + preconfiguredActions: [], }); const savedObjectCreateResult = { @@ -305,6 +309,7 @@ describe('get()', () => { const result = await actionsClient.get({ id: '1' }); expect(result).toEqual({ id: '1', + isPreconfigured: false, }); expect(savedObjectsClient.get).toHaveBeenCalledTimes(1); expect(savedObjectsClient.get.mock.calls[0]).toMatchInlineSnapshot(` @@ -314,9 +319,44 @@ describe('get()', () => { ] `); }); + + test('return predefined action with id', async () => { + actionsClient = new ActionsClient({ + actionTypeRegistry, + savedObjectsClient, + scopedClusterClient, + defaultKibanaIndex, + preconfiguredActions: [ + { + id: 'testPreconfigured', + actionTypeId: '.slack', + secrets: { + test: 'test1', + }, + isPreconfigured: true, + name: 'test', + config: { + foo: 'bar', + }, + }, + ], + }); + + const result = await actionsClient.get({ id: 'testPreconfigured' }); + expect(result).toEqual({ + id: 'testPreconfigured', + actionTypeId: '.slack', + isPreconfigured: true, + name: 'test', + config: { + foo: 'bar', + }, + }); + expect(savedObjectsClient.get).not.toHaveBeenCalled(); + }); }); -describe('find()', () => { +describe('getAll()', () => { test('calls savedObjectsClient with parameters', async () => { const expectedResult = { total: 1, @@ -327,6 +367,7 @@ describe('find()', () => { id: '1', type: 'type', attributes: { + name: 'test', config: { foo: 'bar', }, @@ -339,31 +380,50 @@ describe('find()', () => { scopedClusterClient.callAsInternalUser.mockResolvedValueOnce({ aggregations: { '1': { doc_count: 6 }, + testPreconfigured: { doc_count: 2 }, }, }); - const result = await actionsClient.find({}); - expect(result).toEqual({ - total: 1, - perPage: 10, - page: 1, - data: [ + + actionsClient = new ActionsClient({ + actionTypeRegistry, + savedObjectsClient, + scopedClusterClient, + defaultKibanaIndex, + preconfiguredActions: [ { - id: '1', + id: 'testPreconfigured', + actionTypeId: '.slack', + secrets: {}, + isPreconfigured: true, + name: 'test', config: { foo: 'bar', }, - referencedByCount: 6, }, ], }); - expect(savedObjectsClient.find).toHaveBeenCalledTimes(1); - expect(savedObjectsClient.find.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "type": "action", + const result = await actionsClient.getAll(); + expect(result).toEqual([ + { + id: '1', + isPreconfigured: false, + name: 'test', + config: { + foo: 'bar', }, - ] - `); + referencedByCount: 6, + }, + { + id: 'testPreconfigured', + actionTypeId: '.slack', + isPreconfigured: true, + name: 'test', + config: { + foo: 'bar', + }, + referencedByCount: 2, + }, + ]); }); }); @@ -420,6 +480,7 @@ describe('update()', () => { }); expect(result).toEqual({ id: 'my-action', + isPreconfigured: false, actionTypeId: 'my-action-type', name: 'my name', config: {}, @@ -524,6 +585,7 @@ describe('update()', () => { }); expect(result).toEqual({ id: 'my-action', + isPreconfigured: false, actionTypeId: 'my-action-type', name: 'my name', config: { diff --git a/x-pack/plugins/actions/server/actions_client.ts b/x-pack/plugins/actions/server/actions_client.ts index 129829850f9c1..8f73bfb31ea4d 100644 --- a/x-pack/plugins/actions/server/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client.ts @@ -11,9 +11,16 @@ import { SavedObject, } from 'src/core/server'; +import { i18n } from '@kbn/i18n'; import { ActionTypeRegistry } from './action_type_registry'; import { validateConfig, validateSecrets } from './lib'; -import { ActionResult, FindActionResult, RawAction } from './types'; +import { ActionResult, FindActionResult, RawAction, PreConfiguredAction } from './types'; +import { PreconfiguredActionDisabledModificationError } from './lib/errors/preconfigured_action_disabled_modification'; + +// We are assuming there won't be many actions. This is why we will load +// all the actions in advance and assume the total count to not go over 10000. +// We'll set this max setting assuming it's never reached. +export const MAX_ACTIONS_RETURNED = 10000; interface ActionUpdate extends SavedObjectAttributes { name: string; @@ -29,35 +36,12 @@ interface CreateOptions { action: Action; } -interface FindOptions { - options?: { - perPage?: number; - page?: number; - search?: string; - defaultSearchOperator?: 'AND' | 'OR'; - searchFields?: string[]; - sortField?: string; - hasReference?: { - type: string; - id: string; - }; - fields?: string[]; - filter?: string; - }; -} - -interface FindResult { - page: number; - perPage: number; - total: number; - data: FindActionResult[]; -} - interface ConstructorOptions { defaultKibanaIndex: string; scopedClusterClient: IScopedClusterClient; actionTypeRegistry: ActionTypeRegistry; savedObjectsClient: SavedObjectsClientContract; + preconfiguredActions: PreConfiguredAction[]; } interface UpdateOptions { @@ -70,17 +54,20 @@ export class ActionsClient { private readonly scopedClusterClient: IScopedClusterClient; private readonly savedObjectsClient: SavedObjectsClientContract; private readonly actionTypeRegistry: ActionTypeRegistry; + private readonly preconfiguredActions: PreConfiguredAction[]; constructor({ actionTypeRegistry, defaultKibanaIndex, scopedClusterClient, savedObjectsClient, + preconfiguredActions, }: ConstructorOptions) { this.actionTypeRegistry = actionTypeRegistry; this.savedObjectsClient = savedObjectsClient; this.scopedClusterClient = scopedClusterClient; this.defaultKibanaIndex = defaultKibanaIndex; + this.preconfiguredActions = preconfiguredActions; } /** @@ -106,6 +93,7 @@ export class ActionsClient { actionTypeId: result.attributes.actionTypeId, name: result.attributes.name, config: result.attributes.config, + isPreconfigured: false, }; } @@ -113,6 +101,20 @@ export class ActionsClient { * Update action */ public async update({ id, action }: UpdateOptions): Promise { + if ( + this.preconfiguredActions.find(preconfiguredAction => preconfiguredAction.id === id) !== + undefined + ) { + throw new PreconfiguredActionDisabledModificationError( + i18n.translate('xpack.actions.serverSideErrors.predefinedActionUpdateDisabled', { + defaultMessage: 'Preconfigured action {id} is not allowed to update.', + values: { + id, + }, + }), + 'update' + ); + } const existingObject = await this.savedObjectsClient.get('action', id); const { actionTypeId } = existingObject.attributes; const { name, config, secrets } = action; @@ -134,6 +136,7 @@ export class ActionsClient { actionTypeId: result.attributes.actionTypeId as string, name: result.attributes.name as string, config: result.attributes.config as Record, + isPreconfigured: false, }; } @@ -141,6 +144,18 @@ export class ActionsClient { * Get an action */ public async get({ id }: { id: string }): Promise { + const preconfiguredActionsList = this.preconfiguredActions.find( + preconfiguredAction => preconfiguredAction.id === id + ); + if (preconfiguredActionsList !== undefined) { + return { + id, + actionTypeId: preconfiguredActionsList.actionTypeId, + name: preconfiguredActionsList.name, + config: preconfiguredActionsList.config, + isPreconfigured: true, + }; + } const result = await this.savedObjectsClient.get('action', id); return { @@ -148,36 +163,56 @@ export class ActionsClient { actionTypeId: result.attributes.actionTypeId, name: result.attributes.name, config: result.attributes.config, + isPreconfigured: false, }; } /** - * Find actions + * Get all actions with preconfigured list */ - public async find({ options = {} }: FindOptions): Promise { - const findResult = await this.savedObjectsClient.find({ - ...options, - type: 'action', - }); + public async getAll(): Promise { + const savedObjectsActions = ( + await this.savedObjectsClient.find({ + perPage: MAX_ACTIONS_RETURNED, + type: 'action', + }) + ).saved_objects.map(actionFromSavedObject); - const data = await injectExtraFindData( + const mergedResult = [ + ...savedObjectsActions, + ...this.preconfiguredActions.map(preconfiguredAction => ({ + id: preconfiguredAction.id, + actionTypeId: preconfiguredAction.actionTypeId, + name: preconfiguredAction.name, + config: preconfiguredAction.config, + isPreconfigured: true, + })), + ].sort((a, b) => a.name.localeCompare(b.name)); + return await injectExtraFindData( this.defaultKibanaIndex, this.scopedClusterClient, - findResult.saved_objects.map(actionFromSavedObject) + mergedResult ); - - return { - page: findResult.page, - perPage: findResult.per_page, - total: findResult.total, - data, - }; } /** * Delete action */ public async delete({ id }: { id: string }) { + if ( + this.preconfiguredActions.find(preconfiguredAction => preconfiguredAction.id === id) !== + undefined + ) { + throw new PreconfiguredActionDisabledModificationError( + i18n.translate('xpack.actions.serverSideErrors.predefinedActionDeleteDisabled', { + defaultMessage: 'Preconfigured action {id} is not allowed to delete.', + values: { + id, + }, + }), + 'delete' + ); + } return await this.savedObjectsClient.delete('action', id); } } @@ -186,6 +221,7 @@ function actionFromSavedObject(savedObject: SavedObject): ActionResul return { id: savedObject.id, ...savedObject.attributes, + isPreconfigured: false, }; } diff --git a/x-pack/plugins/actions/server/config.test.ts b/x-pack/plugins/actions/server/config.test.ts index 67b7553c4a736..51e87dbd75b48 100644 --- a/x-pack/plugins/actions/server/config.test.ts +++ b/x-pack/plugins/actions/server/config.test.ts @@ -14,6 +14,44 @@ describe('config validation', () => { "enabledActionTypes": Array [ "*", ], + "preconfigured": Array [], + "whitelistedHosts": Array [ + "*", + ], + } + `); + }); + + test('action with preconfigured actions', () => { + const config: Record = { + preconfigured: [ + { + id: 'my-slack1', + actionTypeId: '.slack', + name: 'Slack #xyz', + config: { + webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz', + }, + }, + ], + }; + expect(configSchema.validate(config)).toMatchInlineSnapshot(` + Object { + "enabled": true, + "enabledActionTypes": Array [ + "*", + ], + "preconfigured": Array [ + Object { + "actionTypeId": ".slack", + "config": Object { + "webhookUrl": "https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz", + }, + "id": "my-slack1", + "name": "Slack #xyz", + "secrets": Object {}, + }, + ], "whitelistedHosts": Array [ "*", ], diff --git a/x-pack/plugins/actions/server/config.ts b/x-pack/plugins/actions/server/config.ts index 9e4795be6c208..1f04efd1941b4 100644 --- a/x-pack/plugins/actions/server/config.ts +++ b/x-pack/plugins/actions/server/config.ts @@ -21,6 +21,18 @@ export const configSchema = schema.object({ defaultValue: [WhitelistedHosts.Any], } ), + preconfigured: schema.arrayOf( + schema.object({ + id: schema.string({ minLength: 1 }), + name: schema.string(), + actionTypeId: schema.string({ minLength: 1 }), + config: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), + secrets: schema.recordOf(schema.string(), schema.any(), { defaultValue: {} }), + }), + { + defaultValue: [], + } + ), }); export type ActionsConfig = TypeOf; diff --git a/x-pack/plugins/actions/server/index.ts b/x-pack/plugins/actions/server/index.ts index eee2ae352fe3d..88553c314112f 100644 --- a/x-pack/plugins/actions/server/index.ts +++ b/x-pack/plugins/actions/server/index.ts @@ -11,7 +11,13 @@ import { ActionsClient as ActionsClientClass } from './actions_client'; export type ActionsClient = PublicMethodsOf; -export { ActionsPlugin, ActionResult, ActionTypeExecutorOptions, ActionType } from './types'; +export { + ActionsPlugin, + ActionResult, + ActionTypeExecutorOptions, + ActionType, + PreConfiguredAction, +} from './types'; export { PluginSetupContract, PluginStartContract } from './plugin'; export const plugin = (initContext: PluginInitializerContext) => new ActionsPlugin(initContext); diff --git a/x-pack/plugins/actions/server/lib/errors/preconfigured_action_disabled_modification.ts b/x-pack/plugins/actions/server/lib/errors/preconfigured_action_disabled_modification.ts new file mode 100644 index 0000000000000..884353e132b9c --- /dev/null +++ b/x-pack/plugins/actions/server/lib/errors/preconfigured_action_disabled_modification.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 { KibanaResponseFactory } from '../../../../../../src/core/server'; +import { ErrorThatHandlesItsOwnResponse } from './types'; + +export type PreconfiguredActionDisabledFrom = 'update' | 'delete'; + +export class PreconfiguredActionDisabledModificationError extends Error + implements ErrorThatHandlesItsOwnResponse { + public readonly disabledFrom: PreconfiguredActionDisabledFrom; + + constructor(message: string, disabledFrom: PreconfiguredActionDisabledFrom) { + super(message); + this.disabledFrom = disabledFrom; + } + + public sendResponse(res: KibanaResponseFactory) { + return res.badRequest({ body: { message: this.message } }); + } +} diff --git a/x-pack/plugins/actions/server/mocks.ts b/x-pack/plugins/actions/server/mocks.ts index 75396f2aad897..bc4268bb69872 100644 --- a/x-pack/plugins/actions/server/mocks.ts +++ b/x-pack/plugins/actions/server/mocks.ts @@ -21,6 +21,7 @@ const createStartMock = () => { execute: jest.fn(), isActionTypeEnabled: jest.fn(), getActionsClientWithRequest: jest.fn().mockResolvedValue(actionsClientMock.create()), + preconfiguredActions: [], }; return mock; }; diff --git a/x-pack/plugins/actions/server/plugin.test.ts b/x-pack/plugins/actions/server/plugin.test.ts index 383f84590fbc6..6215b08df81d4 100644 --- a/x-pack/plugins/actions/server/plugin.test.ts +++ b/x-pack/plugins/actions/server/plugin.test.ts @@ -31,7 +31,34 @@ describe('Actions Plugin', () => { let pluginsSetup: jest.Mocked; beforeEach(() => { - context = coreMock.createPluginInitializerContext(); + context = coreMock.createPluginInitializerContext({ + preconfigured: [ + { + id: 'my-slack1', + actionTypeId: '.slack', + name: 'Slack #xyz', + description: 'Send a message to the #xyz channel', + config: { + webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz', + }, + }, + { + id: 'custom-system-abc-connector', + actionTypeId: 'system-abc-action-type', + description: 'Send a notification to system ABC', + name: 'System ABC', + config: { + xyzConfig1: 'value1', + xyzConfig2: 'value2', + listOfThings: ['a', 'b', 'c', 'd'], + }, + secrets: { + xyzSecret1: 'credential1', + xyzSecret2: 'credential2', + }, + }, + ], + }); plugin = new ActionsPlugin(context); coreSetup = coreMock.createSetup(); @@ -160,7 +187,9 @@ describe('Actions Plugin', () => { let pluginsStart: jest.Mocked; beforeEach(() => { - const context = coreMock.createPluginInitializerContext(); + const context = coreMock.createPluginInitializerContext({ + preconfigured: [], + }); plugin = new ActionsPlugin(context); coreSetup = coreMock.createSetup(); coreStart = coreMock.createStart(); diff --git a/x-pack/plugins/actions/server/plugin.ts b/x-pack/plugins/actions/server/plugin.ts index ce31e62bc9b8e..34c9e7aa9e8b8 100644 --- a/x-pack/plugins/actions/server/plugin.ts +++ b/x-pack/plugins/actions/server/plugin.ts @@ -30,7 +30,7 @@ import { LICENSE_TYPE } from '../../licensing/common/types'; import { SpacesPluginSetup, SpacesServiceSetup } from '../../spaces/server'; import { ActionsConfig } from './config'; -import { Services, ActionType } from './types'; +import { Services, ActionType, PreConfiguredAction } from './types'; import { ActionExecutor, TaskRunnerFactory, LicenseState, ILicenseState } from './lib'; import { ActionsClient } from './actions_client'; import { ActionTypeRegistry } from './action_type_registry'; @@ -44,7 +44,7 @@ import { getActionsConfigurationUtilities } from './actions_config'; import { createActionRoute, deleteActionRoute, - findActionRoute, + getAllActionRoute, getActionRoute, updateActionRoute, listActionTypesRoute, @@ -67,6 +67,7 @@ export interface PluginStartContract { isActionTypeEnabled(id: string): boolean; execute(options: ExecuteOptions): Promise; getActionsClientWithRequest(request: KibanaRequest): Promise>; + preconfiguredActions: PreConfiguredAction[]; } export interface ActionsPluginsSetup { @@ -97,6 +98,7 @@ export class ActionsPlugin implements Plugin, Plugi private eventLogger?: IEventLogger; private isESOUsingEphemeralEncryptionKey?: boolean; private readonly telemetryLogger: Logger; + private readonly preconfiguredActions: PreConfiguredAction[]; constructor(initContext: PluginInitializerContext) { this.config = initContext.config @@ -113,6 +115,7 @@ export class ActionsPlugin implements Plugin, Plugi this.logger = initContext.logger.get('actions'); this.telemetryLogger = initContext.logger.get('telemetry'); + this.preconfiguredActions = []; } public async setup(core: CoreSetup, plugins: ActionsPluginsSetup): Promise { @@ -151,8 +154,14 @@ export class ActionsPlugin implements Plugin, Plugi // get executions count const taskRunnerFactory = new TaskRunnerFactory(actionExecutor); - const actionsConfigUtils = getActionsConfigurationUtilities( - (await this.config) as ActionsConfig + const actionsConfig = (await this.config) as ActionsConfig; + const actionsConfigUtils = getActionsConfigurationUtilities(actionsConfig); + + this.preconfiguredActions.push( + ...actionsConfig.preconfigured.map( + preconfiguredAction => + ({ ...preconfiguredAction, isPreconfigured: true } as PreConfiguredAction) + ) ); const actionTypeRegistry = new ActionTypeRegistry({ taskRunnerFactory, @@ -197,7 +206,7 @@ export class ActionsPlugin implements Plugin, Plugi createActionRoute(router, this.licenseState); deleteActionRoute(router, this.licenseState); getActionRoute(router, this.licenseState); - findActionRoute(router, this.licenseState); + getAllActionRoute(router, this.licenseState); updateActionRoute(router, this.licenseState); listActionTypesRoute(router, this.licenseState); executeActionRoute(router, this.licenseState, actionExecutor); @@ -226,6 +235,7 @@ export class ActionsPlugin implements Plugin, Plugi kibanaIndex, adminClient, isESOUsingEphemeralEncryptionKey, + preconfiguredActions, } = this; actionExecutor!.initialize({ @@ -271,8 +281,10 @@ export class ActionsPlugin implements Plugin, Plugi actionTypeRegistry: actionTypeRegistry!, defaultKibanaIndex: await kibanaIndex, scopedClusterClient: adminClient!.asScoped(request), + preconfiguredActions, }); }, + preconfiguredActions, }; } @@ -289,7 +301,12 @@ export class ActionsPlugin implements Plugin, Plugi private createRouteHandlerContext = ( defaultKibanaIndex: string ): IContextProvider, 'actions'> => { - const { actionTypeRegistry, adminClient, isESOUsingEphemeralEncryptionKey } = this; + const { + actionTypeRegistry, + adminClient, + isESOUsingEphemeralEncryptionKey, + preconfiguredActions, + } = this; return async function actionsRouteHandlerContext(context, request) { return { getActionsClient: () => { @@ -303,6 +320,7 @@ export class ActionsPlugin implements Plugin, Plugi actionTypeRegistry: actionTypeRegistry!, defaultKibanaIndex, scopedClusterClient: adminClient!.asScoped(request), + preconfiguredActions, }); }, listTypes: actionTypeRegistry!.list.bind(actionTypeRegistry!), diff --git a/x-pack/plugins/actions/server/routes/delete.ts b/x-pack/plugins/actions/server/routes/delete.ts index cddebb3a8e31e..ffd1f0faabbab 100644 --- a/x-pack/plugins/actions/server/routes/delete.ts +++ b/x-pack/plugins/actions/server/routes/delete.ts @@ -17,7 +17,7 @@ import { IKibanaResponse, KibanaResponseFactory, } from 'kibana/server'; -import { ILicenseState, verifyApiAccess } from '../lib'; +import { ILicenseState, verifyApiAccess, isErrorThatHandlesItsOwnResponse } from '../lib'; import { BASE_ACTION_API_PATH } from '../../common'; const paramSchema = schema.object({ @@ -46,8 +46,15 @@ export const deleteActionRoute = (router: IRouter, licenseState: ILicenseState) } const actionsClient = context.actions.getActionsClient(); const { id } = req.params; - await actionsClient.delete({ id }); - return res.noContent(); + try { + await actionsClient.delete({ id }); + return res.noContent(); + } catch (e) { + if (isErrorThatHandlesItsOwnResponse(e)) { + return e.sendResponse(res); + } + throw e; + } }) ); }; diff --git a/x-pack/plugins/actions/server/routes/find.test.ts b/x-pack/plugins/actions/server/routes/find.test.ts deleted file mode 100644 index 1b130421fa71f..0000000000000 --- a/x-pack/plugins/actions/server/routes/find.test.ts +++ /dev/null @@ -1,152 +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 { findActionRoute } from './find'; -import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock'; -import { licenseStateMock } from '../lib/license_state.mock'; -import { verifyApiAccess } from '../lib'; -import { mockHandlerArguments } from './_mock_handler_arguments'; - -jest.mock('../lib/verify_api_access.ts', () => ({ - verifyApiAccess: jest.fn(), -})); - -beforeEach(() => { - jest.resetAllMocks(); -}); - -describe('findActionRoute', () => { - it('finds actions with proper parameters', async () => { - const licenseState = licenseStateMock.create(); - const router: RouterMock = mockRouter.create(); - - findActionRoute(router, licenseState); - - const [config, handler] = router.get.mock.calls[0]; - - expect(config.path).toMatchInlineSnapshot(`"/api/action/_find"`); - expect(config.options).toMatchInlineSnapshot(` - Object { - "tags": Array [ - "access:actions-read", - ], - } - `); - - const findResult = { - page: 1, - perPage: 1, - total: 0, - data: [], - }; - const actionsClient = { - find: jest.fn().mockResolvedValueOnce(findResult), - }; - - const [context, req, res] = mockHandlerArguments( - { actionsClient }, - { - query: { - per_page: 1, - page: 1, - default_search_operator: 'OR', - }, - }, - ['ok'] - ); - - expect(await handler(context, req, res)).toMatchInlineSnapshot(` - Object { - "body": Object { - "data": Array [], - "page": 1, - "perPage": 1, - "total": 0, - }, - } - `); - - expect(actionsClient.find).toHaveBeenCalledTimes(1); - expect(actionsClient.find.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - Object { - "options": Object { - "defaultSearchOperator": "OR", - "fields": undefined, - "filter": undefined, - "page": 1, - "perPage": 1, - "search": undefined, - "sortField": undefined, - "sortOrder": undefined, - }, - }, - ] - `); - - expect(res.ok).toHaveBeenCalledWith({ - body: findResult, - }); - }); - - it('ensures the license allows finding actions', async () => { - const licenseState = licenseStateMock.create(); - const router: RouterMock = mockRouter.create(); - - findActionRoute(router, licenseState); - - const [, handler] = router.get.mock.calls[0]; - - const actionsClient = { - find: jest.fn().mockResolvedValueOnce({ - page: 1, - perPage: 1, - total: 0, - data: [], - }), - }; - - const [context, req, res] = mockHandlerArguments(actionsClient, { - query: { - per_page: 1, - page: 1, - default_search_operator: 'OR', - }, - }); - - await handler(context, req, res); - - expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); - }); - - it('ensures the license check prevents finding actions', async () => { - const licenseState = licenseStateMock.create(); - const router: RouterMock = mockRouter.create(); - - (verifyApiAccess as jest.Mock).mockImplementation(() => { - throw new Error('OMG'); - }); - - findActionRoute(router, licenseState); - - const [, handler] = router.get.mock.calls[0]; - - const [context, req, res] = mockHandlerArguments( - {}, - { - query: { - per_page: 1, - page: 1, - default_search_operator: 'OR', - }, - }, - ['ok'] - ); - expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); - - expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); - }); -}); diff --git a/x-pack/plugins/actions/server/routes/find.ts b/x-pack/plugins/actions/server/routes/find.ts deleted file mode 100644 index 45b967629a2a8..0000000000000 --- a/x-pack/plugins/actions/server/routes/find.ts +++ /dev/null @@ -1,95 +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 { schema, TypeOf } from '@kbn/config-schema'; -import { - IRouter, - RequestHandlerContext, - KibanaRequest, - IKibanaResponse, - KibanaResponseFactory, -} from 'kibana/server'; -import { FindOptions } from '../../../alerting/server'; -import { ILicenseState, verifyApiAccess } from '../lib'; -import { BASE_ACTION_API_PATH } from '../../common'; - -// config definition -const querySchema = schema.object({ - per_page: schema.number({ defaultValue: 20, min: 0 }), - page: schema.number({ defaultValue: 1, min: 1 }), - search: schema.maybe(schema.string()), - default_search_operator: schema.oneOf([schema.literal('OR'), schema.literal('AND')], { - defaultValue: 'OR', - }), - search_fields: schema.maybe(schema.oneOf([schema.arrayOf(schema.string()), schema.string()])), - sort_field: schema.maybe(schema.string()), - sort_order: schema.maybe(schema.oneOf([schema.literal('asc'), schema.literal('desc')])), - has_reference: schema.maybe( - // use nullable as maybe is currently broken - // in config-schema - schema.nullable( - schema.object({ - type: schema.string(), - id: schema.string(), - }) - ) - ), - fields: schema.maybe(schema.arrayOf(schema.string())), - filter: schema.maybe(schema.string()), -}); - -export const findActionRoute = (router: IRouter, licenseState: ILicenseState) => { - router.get( - { - path: `${BASE_ACTION_API_PATH}/_find`, - validate: { - query: querySchema, - }, - options: { - tags: ['access:actions-read'], - }, - }, - router.handleLegacyErrors(async function( - context: RequestHandlerContext, - req: KibanaRequest, any, any>, - res: KibanaResponseFactory - ): Promise> { - verifyApiAccess(licenseState); - if (!context.actions) { - return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' }); - } - const actionsClient = context.actions.getActionsClient(); - const query = req.query; - const options: FindOptions['options'] = { - perPage: query.per_page, - page: query.page, - search: query.search, - defaultSearchOperator: query.default_search_operator, - sortField: query.sort_field, - fields: query.fields, - filter: query.filter, - sortOrder: query.sort_order, - }; - - if (query.search_fields) { - options.searchFields = Array.isArray(query.search_fields) - ? query.search_fields - : [query.search_fields]; - } - - if (query.has_reference) { - options.hasReference = query.has_reference; - } - - const findResult = await actionsClient.find({ - options, - }); - return res.ok({ - body: findResult, - }); - }) - ); -}; diff --git a/x-pack/plugins/actions/server/routes/get_all.test.ts b/x-pack/plugins/actions/server/routes/get_all.test.ts new file mode 100644 index 0000000000000..6499427b8c1a5 --- /dev/null +++ b/x-pack/plugins/actions/server/routes/get_all.test.ts @@ -0,0 +1,117 @@ +/* + * 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 { getAllActionRoute } from './get_all'; +import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock'; +import { licenseStateMock } from '../lib/license_state.mock'; +import { verifyApiAccess } from '../lib'; +import { mockHandlerArguments } from './_mock_handler_arguments'; + +jest.mock('../lib/verify_api_access.ts', () => ({ + verifyApiAccess: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('getAllActionRoute', () => { + it('get all actions with proper parameters', async () => { + const licenseState = licenseStateMock.create(); + const router: RouterMock = mockRouter.create(); + + getAllActionRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/action/_getAll"`); + expect(config.options).toMatchInlineSnapshot(` + Object { + "tags": Array [ + "access:actions-read", + ], + } + `); + + const actionsClient = { + getAll: jest.fn().mockResolvedValueOnce([]), + }; + + const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); + + expect(await handler(context, req, res)).toMatchInlineSnapshot(` + Object { + "body": Array [], + } + `); + + expect(actionsClient.getAll).toHaveBeenCalledTimes(1); + + expect(res.ok).toHaveBeenCalledWith({ + body: [], + }); + }); + + it('ensures the license allows getting all actions', async () => { + const licenseState = licenseStateMock.create(); + const router: RouterMock = mockRouter.create(); + + getAllActionRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/action/_getAll"`); + expect(config.options).toMatchInlineSnapshot(` + Object { + "tags": Array [ + "access:actions-read", + ], + } + `); + + const actionsClient = { + getAll: jest.fn().mockResolvedValueOnce([]), + }; + + const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); + + await handler(context, req, res); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); + + it('ensures the license check prevents getting all actions', async () => { + const licenseState = licenseStateMock.create(); + const router: RouterMock = mockRouter.create(); + + (verifyApiAccess as jest.Mock).mockImplementation(() => { + throw new Error('OMG'); + }); + + getAllActionRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/action/_getAll"`); + expect(config.options).toMatchInlineSnapshot(` + Object { + "tags": Array [ + "access:actions-read", + ], + } + `); + + const actionsClient = { + getAll: jest.fn().mockResolvedValueOnce([]), + }; + + const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); + + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); + + expect(verifyApiAccess).toHaveBeenCalledWith(licenseState); + }); +}); diff --git a/x-pack/plugins/actions/server/routes/get_all.ts b/x-pack/plugins/actions/server/routes/get_all.ts new file mode 100644 index 0000000000000..c70a13bc01c9f --- /dev/null +++ b/x-pack/plugins/actions/server/routes/get_all.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + IRouter, + RequestHandlerContext, + KibanaRequest, + IKibanaResponse, + KibanaResponseFactory, +} from 'kibana/server'; +import { ILicenseState, verifyApiAccess } from '../lib'; +import { BASE_ACTION_API_PATH } from '../../common'; + +export const getAllActionRoute = (router: IRouter, licenseState: ILicenseState) => { + router.get( + { + path: `${BASE_ACTION_API_PATH}/_getAll`, + validate: {}, + options: { + tags: ['access:actions-read'], + }, + }, + router.handleLegacyErrors(async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + verifyApiAccess(licenseState); + if (!context.actions) { + return res.badRequest({ body: 'RouteHandlerContext is not registered for actions' }); + } + const actionsClient = context.actions.getActionsClient(); + const result = await actionsClient.getAll(); + return res.ok({ + body: result, + }); + }) + ); +}; diff --git a/x-pack/plugins/actions/server/routes/index.ts b/x-pack/plugins/actions/server/routes/index.ts index 33191132bece5..94f9ec1c94364 100644 --- a/x-pack/plugins/actions/server/routes/index.ts +++ b/x-pack/plugins/actions/server/routes/index.ts @@ -6,7 +6,7 @@ export { createActionRoute } from './create'; export { deleteActionRoute } from './delete'; -export { findActionRoute } from './find'; +export { getAllActionRoute } from './get_all'; export { getActionRoute } from './get'; export { updateActionRoute } from './update'; export { listActionTypesRoute } from './list_action_types'; diff --git a/x-pack/plugins/actions/server/types.ts b/x-pack/plugins/actions/server/types.ts index 999e739e77060..92e38d77314f8 100644 --- a/x-pack/plugins/actions/server/types.ts +++ b/x-pack/plugins/actions/server/types.ts @@ -55,6 +55,11 @@ export interface ActionResult { actionTypeId: string; name: string; config: Record; + isPreconfigured: boolean; +} + +export interface PreConfiguredAction extends ActionResult { + secrets: Record; } export interface FindActionResult extends ActionResult { diff --git a/x-pack/plugins/alerting/common/index.ts b/x-pack/plugins/alerting/common/index.ts index 9d4ea69a63609..2574e73dd4f9a 100644 --- a/x-pack/plugins/alerting/common/index.ts +++ b/x-pack/plugins/alerting/common/index.ts @@ -17,6 +17,7 @@ export interface ActionGroup { export interface AlertingFrameworkHealth { isSufficientlySecure: boolean; + hasPermanentEncryptionKey: boolean; } export const BASE_ALERT_API_PATH = '/api/alert'; diff --git a/x-pack/plugins/alerting/server/alerts_client.test.ts b/x-pack/plugins/alerting/server/alerts_client.test.ts index 0e929ff457fbd..a9ff5ee8ecdc6 100644 --- a/x-pack/plugins/alerting/server/alerts_client.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client.test.ts @@ -30,6 +30,7 @@ const alertsClientParams = { invalidateAPIKey: jest.fn(), logger: loggingServiceMock.create().get(), encryptedSavedObjectsPlugin: encryptedSavedObjects, + preconfiguredActions: [], }; beforeEach(() => { diff --git a/x-pack/plugins/alerting/server/alerts_client.ts b/x-pack/plugins/alerting/server/alerts_client.ts index 5538b44b69fcb..6f8478df58a53 100644 --- a/x-pack/plugins/alerting/server/alerts_client.ts +++ b/x-pack/plugins/alerting/server/alerts_client.ts @@ -13,6 +13,7 @@ import { SavedObjectReference, SavedObject, } from 'src/core/server'; +import { PreConfiguredAction } from '../../actions/server'; import { Alert, PartialAlert, @@ -53,6 +54,7 @@ interface ConstructorOptions { getUserName: () => Promise; createAPIKey: () => Promise; invalidateAPIKey: (params: InvalidateAPIKeyParams) => Promise; + preconfiguredActions: PreConfiguredAction[]; } export interface FindOptions { @@ -123,6 +125,7 @@ export class AlertsClient { private readonly invalidateAPIKey: ( params: InvalidateAPIKeyParams ) => Promise; + private preconfiguredActions: PreConfiguredAction[]; encryptedSavedObjectsPlugin: EncryptedSavedObjectsPluginStart; constructor({ @@ -136,6 +139,7 @@ export class AlertsClient { createAPIKey, invalidateAPIKey, encryptedSavedObjectsPlugin, + preconfiguredActions, }: ConstructorOptions) { this.logger = logger; this.getUserName = getUserName; @@ -147,6 +151,7 @@ export class AlertsClient { this.createAPIKey = createAPIKey; this.invalidateAPIKey = invalidateAPIKey; this.encryptedSavedObjectsPlugin = encryptedSavedObjectsPlugin; + this.preconfiguredActions = preconfiguredActions; } public async create({ data, options }: CreateOptions): Promise { @@ -659,18 +664,37 @@ export class AlertsClient { private async denormalizeActions( alertActions: NormalizedAlertAction[] ): Promise<{ actions: RawAlert['actions']; references: SavedObjectReference[] }> { - // Fetch action objects in bulk - const actionIds = [...new Set(alertActions.map(alertAction => alertAction.id))]; - const bulkGetOpts = actionIds.map(id => ({ id, type: 'action' })); - const bulkGetResult = await this.savedObjectsClient.bulkGet(bulkGetOpts); const actionMap = new Map(); - for (const action of bulkGetResult.saved_objects) { - if (action.error) { - throw Boom.badRequest( - `Failed to load action ${action.id} (${action.error.statusCode}): ${action.error.message}` - ); + // map preconfigured actions + for (const alertAction of alertActions) { + const action = this.preconfiguredActions.find( + preconfiguredAction => preconfiguredAction.id === alertAction.id + ); + if (action !== undefined) { + actionMap.set(action.id, action); + } + } + // Fetch action objects in bulk + // Excluding preconfigured actions to avoid an not found error, which is already mapped + const actionIds = [ + ...new Set( + alertActions + .filter(alertAction => !actionMap.has(alertAction.id)) + .map(alertAction => alertAction.id) + ), + ]; + if (actionIds.length > 0) { + const bulkGetOpts = actionIds.map(id => ({ id, type: 'action' })); + const bulkGetResult = await this.savedObjectsClient.bulkGet(bulkGetOpts); + + for (const action of bulkGetResult.saved_objects) { + if (action.error) { + throw Boom.badRequest( + `Failed to load action ${action.id} (${action.error.statusCode}): ${action.error.message}` + ); + } + actionMap.set(action.id, action); } - actionMap.set(action.id, action); } // Extract references and set actionTypeId const references: SavedObjectReference[] = []; @@ -681,10 +705,16 @@ export class AlertsClient { name: actionRef, type: 'action', }); + const actionMapValue = actionMap.get(id); + // if action is a save object, than actionTypeId should be under attributes property + // if action is a preconfigured, than actionTypeId is the action property + const actionTypeId = actionIds.find(actionId => actionId === id) + ? actionMapValue.attributes.actionTypeId + : actionMapValue.actionTypeId; return { ...alertAction, actionRef, - actionTypeId: actionMap.get(id).attributes.actionTypeId, + actionTypeId, }; }); return { diff --git a/x-pack/plugins/alerting/server/alerts_client_factory.test.ts b/x-pack/plugins/alerting/server/alerts_client_factory.test.ts index 4c74ca54a0d2f..951d18a33b35f 100644 --- a/x-pack/plugins/alerting/server/alerts_client_factory.test.ts +++ b/x-pack/plugins/alerting/server/alerts_client_factory.test.ts @@ -28,6 +28,7 @@ const alertsClientFactoryParams: jest.Mocked = { getSpaceId: jest.fn(), spaceIdToNamespace: jest.fn(), encryptedSavedObjectsPlugin: encryptedSavedObjectsMock.createStart(), + preconfiguredActions: [], }; const fakeRequest: Request = { headers: {}, @@ -67,6 +68,7 @@ test('creates an alerts client with proper constructor arguments', async () => { createAPIKey: expect.any(Function), invalidateAPIKey: expect.any(Function), encryptedSavedObjectsPlugin: alertsClientFactoryParams.encryptedSavedObjectsPlugin, + preconfiguredActions: [], }); }); diff --git a/x-pack/plugins/alerting/server/alerts_client_factory.ts b/x-pack/plugins/alerting/server/alerts_client_factory.ts index fd480658e236a..734417e72733e 100644 --- a/x-pack/plugins/alerting/server/alerts_client_factory.ts +++ b/x-pack/plugins/alerting/server/alerts_client_factory.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import { PreConfiguredAction } from '../../actions/server'; import { AlertsClient } from './alerts_client'; import { AlertTypeRegistry, SpaceIdToNamespaceFunction } from './types'; import { KibanaRequest, Logger, SavedObjectsClientContract } from '../../../../src/core/server'; @@ -19,6 +20,7 @@ export interface AlertsClientFactoryOpts { getSpaceId: (request: KibanaRequest) => string | undefined; spaceIdToNamespace: SpaceIdToNamespaceFunction; encryptedSavedObjectsPlugin: EncryptedSavedObjectsPluginStart; + preconfiguredActions: PreConfiguredAction[]; } export class AlertsClientFactory { @@ -30,6 +32,7 @@ export class AlertsClientFactory { private getSpaceId!: (request: KibanaRequest) => string | undefined; private spaceIdToNamespace!: SpaceIdToNamespaceFunction; private encryptedSavedObjectsPlugin!: EncryptedSavedObjectsPluginStart; + private preconfiguredActions!: PreConfiguredAction[]; public initialize(options: AlertsClientFactoryOpts) { if (this.isInitialized) { @@ -43,6 +46,7 @@ export class AlertsClientFactory { this.securityPluginSetup = options.securityPluginSetup; this.spaceIdToNamespace = options.spaceIdToNamespace; this.encryptedSavedObjectsPlugin = options.encryptedSavedObjectsPlugin; + this.preconfiguredActions = options.preconfiguredActions; } public create( @@ -100,6 +104,7 @@ export class AlertsClientFactory { result: invalidateAPIKeyResult, }; }, + preconfiguredActions: this.preconfiguredActions, }); } } diff --git a/x-pack/plugins/alerting/server/plugin.ts b/x-pack/plugins/alerting/server/plugin.ts index 90e274df3a5ee..fdca6c0a9b503 100644 --- a/x-pack/plugins/alerting/server/plugin.ts +++ b/x-pack/plugins/alerting/server/plugin.ts @@ -190,7 +190,7 @@ export class AlertingPlugin { unmuteAllAlertRoute(router, this.licenseState); muteAlertInstanceRoute(router, this.licenseState); unmuteAlertInstanceRoute(router, this.licenseState); - healthRoute(router, this.licenseState); + healthRoute(router, this.licenseState, plugins.encryptedSavedObjects); return { registerType: alertTypeRegistry.register.bind(alertTypeRegistry), @@ -218,6 +218,7 @@ export class AlertingPlugin { getSpaceId(request: KibanaRequest) { return spaces?.getSpaceId(request); }, + preconfiguredActions: plugins.actions.preconfiguredActions, }); taskRunnerFactory.initialize({ diff --git a/x-pack/plugins/alerting/server/routes/health.test.ts b/x-pack/plugins/alerting/server/routes/health.test.ts index 9efe020bc10c4..42c83a7c04deb 100644 --- a/x-pack/plugins/alerting/server/routes/health.test.ts +++ b/x-pack/plugins/alerting/server/routes/health.test.ts @@ -10,6 +10,7 @@ import { mockHandlerArguments } from './_mock_handler_arguments'; import { elasticsearchServiceMock } from '../../../../../src/core/server/mocks'; import { verifyApiAccess } from '../lib/license_api_access'; import { mockLicenseState } from '../lib/license_state.mock'; +import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/mocks'; jest.mock('../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), @@ -24,7 +25,9 @@ describe('healthRoute', () => { const router: RouterMock = mockRouter.create(); const licenseState = mockLicenseState(); - healthRoute(router, licenseState); + const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup(); + encryptedSavedObjects.usingEphemeralEncryptionKey = false; + healthRoute(router, licenseState, encryptedSavedObjects); const [config] = router.get.mock.calls[0]; @@ -35,7 +38,9 @@ describe('healthRoute', () => { const router: RouterMock = mockRouter.create(); const licenseState = mockLicenseState(); - healthRoute(router, licenseState); + const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup(); + encryptedSavedObjects.usingEphemeralEncryptionKey = false; + healthRoute(router, licenseState, encryptedSavedObjects); const [, handler] = router.get.mock.calls[0]; const elasticsearch = elasticsearchServiceMock.createSetup(); @@ -58,11 +63,37 @@ describe('healthRoute', () => { `); }); + it('evaluates whether Encrypted Saved Objects is using an ephemeral encryption key', async () => { + const router: RouterMock = mockRouter.create(); + + const licenseState = mockLicenseState(); + const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup(); + encryptedSavedObjects.usingEphemeralEncryptionKey = true; + healthRoute(router, licenseState, encryptedSavedObjects); + const [, handler] = router.get.mock.calls[0]; + + const elasticsearch = elasticsearchServiceMock.createSetup(); + elasticsearch.adminClient.callAsInternalUser.mockReturnValue(Promise.resolve({})); + + const [context, req, res] = mockHandlerArguments({ elasticsearch }, {}, ['ok']); + + expect(await handler(context, req, res)).toMatchInlineSnapshot(` + Object { + "body": Object { + "hasPermanentEncryptionKey": false, + "isSufficientlySecure": true, + }, + } + `); + }); + it('evaluates missing security info from the usage api to mean that the security plugin is disbled', async () => { const router: RouterMock = mockRouter.create(); const licenseState = mockLicenseState(); - healthRoute(router, licenseState); + const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup(); + encryptedSavedObjects.usingEphemeralEncryptionKey = false; + healthRoute(router, licenseState, encryptedSavedObjects); const [, handler] = router.get.mock.calls[0]; const elasticsearch = elasticsearchServiceMock.createSetup(); @@ -73,6 +104,7 @@ describe('healthRoute', () => { expect(await handler(context, req, res)).toMatchInlineSnapshot(` Object { "body": Object { + "hasPermanentEncryptionKey": true, "isSufficientlySecure": true, }, } @@ -83,7 +115,9 @@ describe('healthRoute', () => { const router: RouterMock = mockRouter.create(); const licenseState = mockLicenseState(); - healthRoute(router, licenseState); + const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup(); + encryptedSavedObjects.usingEphemeralEncryptionKey = false; + healthRoute(router, licenseState, encryptedSavedObjects); const [, handler] = router.get.mock.calls[0]; const elasticsearch = elasticsearchServiceMock.createSetup(); @@ -94,6 +128,7 @@ describe('healthRoute', () => { expect(await handler(context, req, res)).toMatchInlineSnapshot(` Object { "body": Object { + "hasPermanentEncryptionKey": true, "isSufficientlySecure": true, }, } @@ -104,7 +139,9 @@ describe('healthRoute', () => { const router: RouterMock = mockRouter.create(); const licenseState = mockLicenseState(); - healthRoute(router, licenseState); + const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup(); + encryptedSavedObjects.usingEphemeralEncryptionKey = false; + healthRoute(router, licenseState, encryptedSavedObjects); const [, handler] = router.get.mock.calls[0]; const elasticsearch = elasticsearchServiceMock.createSetup(); @@ -117,6 +154,7 @@ describe('healthRoute', () => { expect(await handler(context, req, res)).toMatchInlineSnapshot(` Object { "body": Object { + "hasPermanentEncryptionKey": true, "isSufficientlySecure": false, }, } @@ -127,7 +165,9 @@ describe('healthRoute', () => { const router: RouterMock = mockRouter.create(); const licenseState = mockLicenseState(); - healthRoute(router, licenseState); + const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup(); + encryptedSavedObjects.usingEphemeralEncryptionKey = false; + healthRoute(router, licenseState, encryptedSavedObjects); const [, handler] = router.get.mock.calls[0]; const elasticsearch = elasticsearchServiceMock.createSetup(); @@ -140,6 +180,7 @@ describe('healthRoute', () => { expect(await handler(context, req, res)).toMatchInlineSnapshot(` Object { "body": Object { + "hasPermanentEncryptionKey": true, "isSufficientlySecure": false, }, } @@ -150,7 +191,9 @@ describe('healthRoute', () => { const router: RouterMock = mockRouter.create(); const licenseState = mockLicenseState(); - healthRoute(router, licenseState); + const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup(); + encryptedSavedObjects.usingEphemeralEncryptionKey = false; + healthRoute(router, licenseState, encryptedSavedObjects); const [, handler] = router.get.mock.calls[0]; const elasticsearch = elasticsearchServiceMock.createSetup(); @@ -163,6 +206,7 @@ describe('healthRoute', () => { expect(await handler(context, req, res)).toMatchInlineSnapshot(` Object { "body": Object { + "hasPermanentEncryptionKey": true, "isSufficientlySecure": true, }, } diff --git a/x-pack/plugins/alerting/server/routes/health.ts b/x-pack/plugins/alerting/server/routes/health.ts index 29c2f3c5730f4..fa2358a1f181c 100644 --- a/x-pack/plugins/alerting/server/routes/health.ts +++ b/x-pack/plugins/alerting/server/routes/health.ts @@ -14,6 +14,7 @@ import { import { LicenseState } from '../lib/license_state'; import { verifyApiAccess } from '../lib/license_api_access'; import { AlertingFrameworkHealth } from '../types'; +import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server'; interface XPackUsageSecurity { security?: { @@ -26,7 +27,11 @@ interface XPackUsageSecurity { }; } -export function healthRoute(router: IRouter, licenseState: LicenseState) { +export function healthRoute( + router: IRouter, + licenseState: LicenseState, + encryptedSavedObjects: EncryptedSavedObjectsPluginSetup +) { router.get( { path: '/api/alert/_health', @@ -54,6 +59,7 @@ export function healthRoute(router: IRouter, licenseState: LicenseState) { const frameworkHealth: AlertingFrameworkHealth = { isSufficientlySecure: !isSecurityEnabled || (isSecurityEnabled && isTLSEnabled), + hasPermanentEncryptionKey: !encryptedSavedObjects.usingEphemeralEncryptionKey, }; return res.ok({ diff --git a/x-pack/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap b/x-pack/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap index 5de82a9ee8788..54dd4704edfc0 100644 --- a/x-pack/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap +++ b/x-pack/plugins/apm/common/__snapshots__/elasticsearch_fieldnames.test.ts.snap @@ -16,6 +16,8 @@ exports[`Error ERROR_EXC_HANDLED 1`] = `undefined`; exports[`Error ERROR_EXC_MESSAGE 1`] = `undefined`; +exports[`Error ERROR_EXC_TYPE 1`] = `undefined`; + exports[`Error ERROR_GROUP_ID 1`] = `"grouping key"`; exports[`Error ERROR_LOG_LEVEL 1`] = `undefined`; @@ -144,6 +146,8 @@ exports[`Span ERROR_EXC_HANDLED 1`] = `undefined`; exports[`Span ERROR_EXC_MESSAGE 1`] = `undefined`; +exports[`Span ERROR_EXC_TYPE 1`] = `undefined`; + exports[`Span ERROR_GROUP_ID 1`] = `undefined`; exports[`Span ERROR_LOG_LEVEL 1`] = `undefined`; @@ -272,6 +276,8 @@ exports[`Transaction ERROR_EXC_HANDLED 1`] = `undefined`; exports[`Transaction ERROR_EXC_MESSAGE 1`] = `undefined`; +exports[`Transaction ERROR_EXC_TYPE 1`] = `undefined`; + exports[`Transaction ERROR_GROUP_ID 1`] = `undefined`; exports[`Transaction ERROR_LOG_LEVEL 1`] = `undefined`; diff --git a/x-pack/plugins/apm/common/agent_configuration/amount_and_unit.ts b/x-pack/plugins/apm/common/agent_configuration/amount_and_unit.ts index 447e529c9c199..d6520ae150539 100644 --- a/x-pack/plugins/apm/common/agent_configuration/amount_and_unit.ts +++ b/x-pack/plugins/apm/common/agent_configuration/amount_and_unit.ts @@ -10,7 +10,8 @@ interface AmountAndUnit { } export function amountAndUnitToObject(value: string): AmountAndUnit { - const [, amount = '', unit = ''] = value.match(/(\d+)?(\w+)?/) || []; + // matches any postive and negative number and its unit. + const [, amount = '', unit = ''] = value.match(/(^-?\d+)?(\w+)?/) || []; return { amount, unit }; } diff --git a/x-pack/plugins/apm/common/agent_configuration/runtime_types/duration_rt.test.ts b/x-pack/plugins/apm/common/agent_configuration/runtime_types/duration_rt.test.ts index a83ee9262cad6..98d0cb5f028c3 100644 --- a/x-pack/plugins/apm/common/agent_configuration/runtime_types/duration_rt.test.ts +++ b/x-pack/plugins/apm/common/agent_configuration/runtime_types/duration_rt.test.ts @@ -10,24 +10,56 @@ * you may not use this file except in compliance with the Elastic License. */ -import { durationRt } from './duration_rt'; +import { durationRt, getDurationRt } from './duration_rt'; import { isRight } from 'fp-ts/lib/Either'; describe('durationRt', () => { describe('it should not accept', () => { - [undefined, null, '', 0, 'foo', true, false, '100', 's', 'm', '0h'].map( + [ + undefined, + null, + '', + 0, + 'foo', + true, + false, + '100', + 's', + 'm', + '0ms', + '-1ms' + ].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(durationRt.decode(input))).toBe(false); + }); + }); + }); + + describe('it should accept', () => { + ['1000ms', '2s', '3m', '1s'].map(input => { + it(`${JSON.stringify(input)}`, () => { + expect(isRight(durationRt.decode(input))).toBe(true); + }); + }); + }); +}); + +describe('getDurationRt', () => { + const customDurationRt = getDurationRt({ min: -1 }); + describe('it should not accept', () => { + [undefined, null, '', 0, 'foo', true, false, '100', 's', 'm', '-2ms'].map( input => { it(`${JSON.stringify(input)}`, () => { - expect(isRight(durationRt.decode(input))).toBe(false); + expect(isRight(customDurationRt.decode(input))).toBe(false); }); } ); }); - describe('It should accept', () => { - ['1000ms', '2s', '3m'].map(input => { + describe('it should accept', () => { + ['1000ms', '2s', '3m', '1s', '-1s', '0ms'].map(input => { it(`${JSON.stringify(input)}`, () => { - expect(isRight(durationRt.decode(input))).toBe(true); + expect(isRight(customDurationRt.decode(input))).toBe(true); }); }); }); diff --git a/x-pack/plugins/apm/common/agent_configuration/runtime_types/duration_rt.ts b/x-pack/plugins/apm/common/agent_configuration/runtime_types/duration_rt.ts index 383fd69be9a78..b691276854fb0 100644 --- a/x-pack/plugins/apm/common/agent_configuration/runtime_types/duration_rt.ts +++ b/x-pack/plugins/apm/common/agent_configuration/runtime_types/duration_rt.ts @@ -10,24 +10,28 @@ import { amountAndUnitToObject } from '../amount_and_unit'; export const DURATION_UNITS = ['ms', 's', 'm']; -export const durationRt = new t.Type( - 'durationRt', - t.string.is, - (input, context) => { - return either.chain(t.string.validate(input, context), inputAsString => { - const { amount, unit } = amountAndUnitToObject(inputAsString); - const amountAsInt = parseInt(amount, 10); - const isValidUnit = DURATION_UNITS.includes(unit); - const isValid = amountAsInt > 0 && isValidUnit; +export function getDurationRt({ min }: { min: number }) { + return new t.Type( + 'durationRt', + t.string.is, + (input, context) => { + return either.chain(t.string.validate(input, context), inputAsString => { + const { amount, unit } = amountAndUnitToObject(inputAsString); + const amountAsInt = parseInt(amount, 10); + const isValidUnit = DURATION_UNITS.includes(unit); + const isValid = amountAsInt >= min && isValidUnit; - return isValid - ? t.success(inputAsString) - : t.failure( - input, - context, - `Must have numeric amount and a valid unit (${DURATION_UNITS})` - ); - }); - }, - t.identity -); + return isValid + ? t.success(inputAsString) + : t.failure( + input, + context, + `Must have numeric amount and a valid unit (${DURATION_UNITS})` + ); + }); + }, + t.identity + ); +} + +export const durationRt = getDurationRt({ min: 1 }); diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap index 4b74b07fc8e27..49840d2157af7 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/__snapshots__/index.test.ts.snap @@ -118,6 +118,7 @@ Array [ }, Object { "key": "span_frames_min_duration", + "min": -1, "type": "duration", "units": Array [ "ms", @@ -152,8 +153,9 @@ Array [ }, Object { "key": "stress_monitor_gc_stress_threshold", - "type": "boolean", - "validationName": "(\\"true\\" | \\"false\\")", + "type": "float", + "validationError": "Must be a number between 0.000 and 1", + "validationName": "numberFloatRt", }, Object { "key": "stress_monitor_system_cpu_relief_threshold", diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts index 152db37a1bff3..e73aed35e87f9 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/general_settings.ts @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; import { getIntegerRt } from '../runtime_types/integer_rt'; import { captureBodyRt } from '../runtime_types/capture_body_rt'; import { RawSettingDefinition } from './types'; +import { getDurationRt } from '../runtime_types/duration_rt'; /* * Settings added here will show up in the UI and will be validated on the client and server @@ -62,7 +63,7 @@ export const generalSettings: RawSettingDefinition[] = [ 'xpack.apm.agentConfig.captureBody.description', { defaultMessage: - 'For transactions that are HTTP requests, the agent can optionally capture the request body (e.g. POST variables).' + 'For transactions that are HTTP requests, the agent can optionally capture the request body (e.g. POST variables).\nFor transactions that are initiated by receiving a message from a message broker, the agent can capture the textual message body.' } ), options: [ @@ -86,7 +87,7 @@ export const generalSettings: RawSettingDefinition[] = [ 'xpack.apm.agentConfig.captureHeaders.description', { defaultMessage: - 'If set to `true`, the agent will capture request and response headers, including cookies.\n\nNOTE: Setting this to `false` reduces network bandwidth, disk space and object allocations.' + 'If set to `true`, the agent will capture HTTP request and response headers (including cookies), as well as message headers/properties when using messaging frameworks (like Kafka).\n\nNOTE: Setting this to `false` reduces network bandwidth, disk space and object allocations.' } ), excludeAgents: ['js-base', 'rum-js', 'nodejs'] @@ -116,7 +117,7 @@ export const generalSettings: RawSettingDefinition[] = [ }), description: i18n.translate('xpack.apm.agentConfig.recording.description', { defaultMessage: - 'When recording, the agent instruments incoming HTTP requests, tracks errors, and collects and sends metrics. When inactive, the agent works as a noop, not collecting data and not communicating with the APM Server except for polling for updated configuration. As this is a reversible switch, agent threads are not being killed when inactivated, but they will be mostly idle in this state, so the overhead should be negligible. You can use this setting to dynamically control whether Elastic APM is enabled or disabled.' + 'When recording, the agent instruments incoming HTTP requests, tracks errors, and collects and sends metrics. When set to non-recording, the agent works as a noop, not collecting data and not communicating with the APM Server except for polling for updated configuration. As this is a reversible switch, agent threads are not being killed when set to non-recording, but they will be mostly idle in this state, so the overhead should be negligible. You can use this setting to dynamically control whether Elastic APM is enabled or disabled.' }), excludeAgents: ['nodejs'] }, @@ -143,6 +144,7 @@ export const generalSettings: RawSettingDefinition[] = [ { key: 'span_frames_min_duration', type: 'duration', + validation: getDurationRt({ min: -1 }), defaultValue: '5ms', label: i18n.translate('xpack.apm.agentConfig.spanFramesMinDuration.label', { defaultMessage: 'Span frames minimum duration' @@ -154,7 +156,8 @@ export const generalSettings: RawSettingDefinition[] = [ 'In its default settings, the APM agent will collect a stack trace with every recorded span.\nWhile this is very helpful to find the exact place in your code that causes the span, collecting this stack trace does have some overhead. \nWhen setting this option to a negative value, like `-1ms`, stack traces will be collected for all spans. Setting it to a positive value, e.g. `5ms`, will limit stack trace collection to spans with durations equal to or longer than the given value, e.g. 5 milliseconds.\n\nTo disable stack trace collection for spans completely, set the value to `0ms`.' } ), - excludeAgents: ['js-base', 'rum-js', 'nodejs'] + excludeAgents: ['js-base', 'rum-js', 'nodejs'], + min: -1 }, // STACK_TRACE_LIMIT @@ -212,7 +215,7 @@ export const generalSettings: RawSettingDefinition[] = [ 'xpack.apm.agentConfig.transactionSampleRate.description', { defaultMessage: - 'By default, the agent will sample every transaction (e.g. request to your service). To reduce overhead and storage requirements, you can set the sample rate to a value between 0.0 and 1.0. We still record overall time and the result for unsampled transactions, but no context information, labels, or spans.' + 'By default, the agent will sample every transaction (e.g. request to your service). To reduce overhead and storage requirements, you can set the sample rate to a value between 0.0 and 1.0. We still record overall time and the result for unsampled transactions, but not context information, labels, or spans.' } ) } diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/java_settings.ts b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/java_settings.ts index bb050076b9f9a..2e10c74378549 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/java_settings.ts +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/java_settings.ts @@ -20,7 +20,7 @@ export const javaSettings: RawSettingDefinition[] = [ 'xpack.apm.agentConfig.enableLogCorrelation.description', { defaultMessage: - "A boolean specifying if the agent should integrate into SLF4J's MDC to enable trace-log correlation. If set to `true`, the agent will set the `trace.id` and `transaction.id` for the currently active spans and transactions to the MDC. While it's allowed to enable this setting at runtime, you can't disable it without a restart." + "A boolean specifying if the agent should integrate into SLF4J's MDC to enable trace-log correlation. If set to `true`, the agent will set the `trace.id` and `transaction.id` for the currently active spans and transactions to the MDC. Since Java agent version 1.16.0, the agent also adds `error.id` of captured error to the MDC just before the error message is logged. NOTE: While it's allowed to enable this setting at runtime, you can't disable it without a restart." } ), includeAgents: ['java'] @@ -41,7 +41,7 @@ export const javaSettings: RawSettingDefinition[] = [ 'xpack.apm.agentConfig.circuitBreakerEnabled.description', { defaultMessage: - 'A boolean specifying whether the circuit breaker should be enabled or not. When enabled, the agent periodically polls stress monitors to detect system/process/JVM stress state. If ANY of the monitors detects a stress indication, the agent will become inactive, as if the `active` configuration option has been set to `false`, thus reducing resource consumption to a minimum. When inactive, the agent continues polling the same monitors in order to detect whether the stress state has been relieved. If ALL monitors approve that the system/process/JVM is not under stress anymore, the agent will resume and become fully functional.' + 'A boolean specifying whether the circuit breaker should be enabled or not. When enabled, the agent periodically polls stress monitors to detect system/process/JVM stress state. If ANY of the monitors detects a stress indication, the agent will pause, as if the `recording` configuration option has been set to `false`, thus reducing resource consumption to a minimum. When paused, the agent continues polling the same monitors in order to detect whether the stress state has been relieved. If ALL monitors approve that the system/process/JVM is not under stress anymore, the agent will resume and become fully functional.' } ), includeAgents: ['java'] @@ -52,7 +52,7 @@ export const javaSettings: RawSettingDefinition[] = [ 'xpack.apm.agentConfig.stressMonitorGcStressThreshold.label', { defaultMessage: 'Stress monitor gc stress threshold' } ), - type: 'boolean', + type: 'float', category: 'Circuit-Breaker', defaultValue: '0.95', description: i18n.translate( @@ -155,7 +155,7 @@ export const javaSettings: RawSettingDefinition[] = [ 'xpack.apm.agentConfig.profilingInferredSpansEnabled.description', { defaultMessage: - 'Set to `true` to make the agent create spans for method executions based on async-profiler, a sampling aka statistical profiler. Due to the nature of how sampling profilers work, the duration of the inferred spans are not exact, but only estimations. The `profiling_inferred_spans_sampling_interval` lets you fine tune the trade-off between accuracy and overhead. The inferred spans are created after a profiling session has ended. This means there is a delay between the regular and the inferred spans being visible in the UI. This feature is not available on Windows' + 'Set to `true` to make the agent create spans for method executions based on async-profiler, a sampling aka statistical profiler. Due to the nature of how sampling profilers work, the duration of the inferred spans are not exact, but only estimations. The `profiling_inferred_spans_sampling_interval` lets you fine tune the trade-off between accuracy and overhead. The inferred spans are created after a profiling session has ended. This means there is a delay between the regular and the inferred spans being visible in the UI. NOTE: This feature is not available on Windows.' } ), includeAgents: ['java'] @@ -209,7 +209,7 @@ export const javaSettings: RawSettingDefinition[] = [ 'xpack.apm.agentConfig.profilingInferredSpansIncludedClasses.description', { defaultMessage: - 'If set, the agent will only create inferred spans for methods which match this list. Setting a value may slightly increase performance and can reduce clutter by only creating spans for the classes you are interested in. Example: `org.example.myapp.*` This option supports the wildcard `*`, which matches zero or more characters. Examples: `/foo/*/bar/*/baz*`, `*foo*`. Matching is case insensitive by default. Prepending an element with `(?-i)` makes the matching case sensitive.' + 'If set, the agent will only create inferred spans for methods which match this list. Setting a value may slightly reduce overhead and can reduce clutter by only creating spans for the classes you are interested in. This option supports the wildcard `*`, which matches zero or more characters. Example: `org.example.myapp.*`. Matching is case insensitive by default. Prepending an element with `(?-i)` makes the matching case sensitive.' } ), includeAgents: ['java'] @@ -228,7 +228,7 @@ export const javaSettings: RawSettingDefinition[] = [ 'xpack.apm.agentConfig.profilingInferredSpansExcludedClasses.description', { defaultMessage: - 'Excludes classes for which no profiler-inferred spans should be created. This option supports the wildcard `*`, which matches zero or more characters. Examples: `/foo/*/bar/*/baz*`, `*foo*`. Matching is case insensitive by default. Prepending an element with `(?-i)` makes the matching case sensitive.' + 'Excludes classes for which no profiler-inferred spans should be created. This option supports the wildcard `*`, which matches zero or more characters. Matching is case insensitive by default. Prepending an element with `(?-i)` makes the matching case sensitive.' } ), includeAgents: ['java'] diff --git a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/types.d.ts b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/types.d.ts index 6b584fc7e2048..282ced346dda0 100644 --- a/x-pack/plugins/apm/common/agent_configuration/setting_definitions/types.d.ts +++ b/x-pack/plugins/apm/common/agent_configuration/setting_definitions/types.d.ts @@ -91,6 +91,7 @@ interface BytesSetting extends BaseSetting { interface DurationSetting extends BaseSetting { type: 'duration'; units?: string[]; + min?: number; } export type RawSettingDefinition = diff --git a/x-pack/plugins/apm/common/elasticsearch_fieldnames.ts b/x-pack/plugins/apm/common/elasticsearch_fieldnames.ts index bc1b346f50da7..d5c3f91eb9247 100644 --- a/x-pack/plugins/apm/common/elasticsearch_fieldnames.ts +++ b/x-pack/plugins/apm/common/elasticsearch_fieldnames.ts @@ -60,6 +60,7 @@ export const ERROR_LOG_LEVEL = 'error.log.level'; export const ERROR_LOG_MESSAGE = 'error.log.message'; export const ERROR_EXC_MESSAGE = 'error.exception.message'; // only to be used in es queries, since error.exception is now an array export const ERROR_EXC_HANDLED = 'error.exception.handled'; // only to be used in es queries, since error.exception is now an array +export const ERROR_EXC_TYPE = 'error.exception.type'; export const ERROR_PAGE_URL = 'error.page.url'; // METRICS diff --git a/x-pack/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap index b9ac9d5431700..982ad558dc91d 100644 --- a/x-pack/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap @@ -73,6 +73,7 @@ Object { "error.log.message", "error.exception.message", "error.exception.handled", + "error.exception.type", "error.culprit", "error.grouping_key", "@timestamp", @@ -148,6 +149,7 @@ Object { "error.log.message", "error.exception.message", "error.exception.handled", + "error.exception.type", "error.culprit", "error.grouping_key", "@timestamp", diff --git a/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts b/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts index 8ea6df5a9898a..5221d737866f4 100644 --- a/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts +++ b/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts @@ -8,6 +8,7 @@ import { ERROR_CULPRIT, ERROR_EXC_HANDLED, ERROR_EXC_MESSAGE, + ERROR_EXC_TYPE, ERROR_GROUP_ID, ERROR_LOG_MESSAGE } from '../../../common/elasticsearch_fieldnames'; @@ -67,6 +68,7 @@ export async function getErrorGroups({ ERROR_LOG_MESSAGE, ERROR_EXC_MESSAGE, ERROR_EXC_HANDLED, + ERROR_EXC_TYPE, ERROR_CULPRIT, ERROR_GROUP_ID, '@timestamp' @@ -99,6 +101,7 @@ export async function getErrorGroups({ exception?: Array<{ handled?: boolean; message?: string; + type?: string; }>; culprit: APMError['error']['culprit']; grouping_key: APMError['error']['grouping_key']; @@ -120,7 +123,8 @@ export async function getErrorGroups({ culprit: source.error.culprit, groupId: source.error.grouping_key, latestOccurrenceAt: source['@timestamp'], - handled: source.error.exception?.[0].handled + handled: source.error.exception?.[0].handled, + type: source.error.exception?.[0].type }; }); diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts index 0fe825e8ace35..d7e28828572d5 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts @@ -41,9 +41,7 @@ export async function getServiceMapServiceNodeInfo({ const filter: ESFilter[] = [ { range: rangeFilter(start, end) }, { term: { [SERVICE_NAME]: serviceName } }, - ...(environment - ? [{ term: { [SERVICE_ENVIRONMENT]: SERVICE_ENVIRONMENT } }] - : []) + ...(environment ? [{ term: { [SERVICE_ENVIRONMENT]: environment } }] : []) ]; const minutes = Math.abs((end - start) / (1000 * 60)); diff --git a/x-pack/plugins/case/common/api/cases/configure.ts b/x-pack/plugins/case/common/api/cases/configure.ts index 9b210c2aa05ad..d92af587d0e92 100644 --- a/x-pack/plugins/case/common/api/cases/configure.ts +++ b/x-pack/plugins/case/common/api/cases/configure.ts @@ -61,13 +61,6 @@ export type CasesConnectorConfiguration = rt.TypeOf action.actionTypeId === CASE_SERVICE_NOW_ACTION + ); + return response.ok({ body: results }); } catch (error) { return response.customError(wrapError(error)); } diff --git a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.test.ts b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.test.ts index 88c576c70bdf0..a8a42fe11e7ce 100644 --- a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.test.ts +++ b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.test.ts @@ -71,7 +71,7 @@ describe('ES search strategy', () => { expect(mockApiCaller.mock.calls[0][0]).toBe('transport.request'); const { method, path, body } = mockApiCaller.mock.calls[0][1]; expect(method).toBe('POST'); - expect(path).toBe('logstash-*/_async_search'); + expect(path).toBe('/logstash-*/_async_search'); expect(body).toEqual({ query: {} }); }); @@ -94,7 +94,7 @@ describe('ES search strategy', () => { expect(mockApiCaller.mock.calls[0][0]).toBe('transport.request'); const { method, path, body } = mockApiCaller.mock.calls[0][1]; expect(method).toBe('GET'); - expect(path).toBe('_async_search/foo'); + expect(path).toBe('/_async_search/foo'); expect(body).toEqual(undefined); }); @@ -117,7 +117,7 @@ describe('ES search strategy', () => { expect(mockApiCaller.mock.calls[0][0]).toBe('transport.request'); const { method, path } = mockApiCaller.mock.calls[0][1]; expect(method).toBe('POST'); - expect(path).toBe('foo-%E7%A8%8B/_async_search'); + expect(path).toBe('/foo-%E7%A8%8B/_async_search'); }); it('calls the rollup API if the index is a rollup type', async () => { @@ -139,6 +139,6 @@ describe('ES search strategy', () => { expect(mockApiCaller.mock.calls[0][0]).toBe('transport.request'); const { method, path } = mockApiCaller.mock.calls[0][1]; expect(method).toBe('POST'); - expect(path).toBe('foo-%E7%A8%8B/_rollup_search'); + expect(path).toBe('/foo-%E7%A8%8B/_rollup_search'); }); }); diff --git a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts index 301f184af7d81..6b329bccab4a7 100644 --- a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts @@ -45,7 +45,7 @@ export const enhancedEsSearchStrategyProvider: TSearchStrategyProvider = async id => { const method = 'DELETE'; - const path = encodeURI(`_async_search/${id}`); + const path = encodeURI(`/_async_search/${id}`); await caller('transport.request', { method, path }); }; @@ -66,7 +66,7 @@ async function asyncSearch( const { body = undefined, index = undefined, ...queryParams } = request.id ? {} : params; const method = request.id ? 'GET' : 'POST'; - const path = encodeURI(request.id ? `_async_search/${request.id}` : `${index}/_async_search`); + const path = encodeURI(request.id ? `/_async_search/${request.id}` : `/${index}/_async_search`); // Wait up to 1s for the response to return const query = toSnakeCase({ waitForCompletionTimeout: '1s', ...queryParams }); @@ -87,7 +87,7 @@ async function rollupSearch( ) { const { body, index, ...params } = request.params; const method = 'POST'; - const path = encodeURI(`${index}/_rollup_search`); + const path = encodeURI(`/${index}/_rollup_search`); const query = toSnakeCase(params); const rawResponse = await ((caller( diff --git a/x-pack/plugins/endpoint/common/generate_data.ts b/x-pack/plugins/endpoint/common/generate_data.ts index 0ec105129b7ac..7c24bd9d77148 100644 --- a/x-pack/plugins/endpoint/common/generate_data.ts +++ b/x-pack/plugins/endpoint/common/generate_data.ts @@ -7,6 +7,11 @@ import uuid from 'uuid'; import seedrandom from 'seedrandom'; import { AlertEvent, EndpointEvent, HostMetadata, OSFields, HostFields } from './types'; +// FIXME: move types/model to top-level +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { PolicyData } from '../public/applications/endpoint/types'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { generatePolicy } from '../public/applications/endpoint/models/policy'; export type Event = AlertEvent | EndpointEvent; @@ -452,6 +457,39 @@ export class EndpointDocGenerator { } } + /** + * Generates an Ingest `datasource` that includes the Endpoint Policy data + */ + public generatePolicyDatasource(): PolicyData { + return { + id: this.seededUUIDv4(), + name: 'Endpoint Policy', + description: 'Policy to protect the worlds data', + config_id: this.seededUUIDv4(), + enabled: true, + output_id: '', + inputs: [ + { + type: 'endpoint', + enabled: true, + streams: [], + config: { + policy: { + value: generatePolicy(), + }, + }, + }, + ], + namespace: 'default', + package: { + name: 'endpoint', + title: 'Elastic Endpoint', + version: '1.0.0', + }, + revision: 1, + }; + } + private randomN(n: number): number { return Math.floor(this.random() * n); } diff --git a/x-pack/plugins/endpoint/common/types.ts b/x-pack/plugins/endpoint/common/types.ts index e8e1281a88925..a614526d92a3f 100644 --- a/x-pack/plugins/endpoint/common/types.ts +++ b/x-pack/plugins/endpoint/common/types.ts @@ -86,7 +86,7 @@ export interface AlertResultList { export interface HostResultList { /* the hosts restricted by the page size */ - hosts: HostMetadata[]; + hosts: HostInfo[]; /* the total number of unique hosts in the index */ total: number; /* the page size requested */ @@ -252,6 +252,32 @@ export type AlertData = AlertEvent & AlertMetadata; export type AlertDetails = AlertData & AlertState; +/** + * The status of the host + */ +export enum HostStatus { + /** + * Default state of the host when no host information is present or host information cannot + * be retrieved. e.g. API error + */ + ERROR = 'error', + + /** + * Host is online as indicated by its checkin status during the last checkin window + */ + ONLINE = 'online', + + /** + * Host is offline as indicated by its checkin status during the last checkin window + */ + OFFLINE = 'offline', +} + +export type HostInfo = Immutable<{ + metadata: HostMetadata; + host_status: HostStatus; +}>; + export type HostMetadata = Immutable<{ '@timestamp': number; event: { diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx index fa9055e0d9bbd..89a6302351a54 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/index.tsx @@ -7,13 +7,9 @@ import * as React from 'react'; import ReactDOM from 'react-dom'; import { CoreStart, AppMountParameters, ScopedHistory } from 'kibana/public'; -import { I18nProvider, FormattedMessage } from '@kbn/i18n/react'; -import { Route, Switch, Router } from 'react-router-dom'; -import { Provider } from 'react-redux'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { Route, Switch } from 'react-router-dom'; import { Store } from 'redux'; -import { useObservable } from 'react-use'; -import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public'; -import { RouteCapture } from './view/route_capture'; import { EndpointPluginStartDependencies } from '../../plugin'; import { appStoreFactory } from './store'; import { AlertIndex } from './view/alerts'; @@ -21,7 +17,7 @@ import { HostList } from './view/hosts'; import { PolicyList } from './view/policy'; import { PolicyDetails } from './view/policy'; import { HeaderNavigation } from './components/header_nav'; -import { EuiThemeProvider } from '../../../../../legacy/common/eui_styled_components'; +import { AppRootProvider } from './view/app_root_provider'; /** * This module will be loaded asynchronously to reduce the bundle size of your plugin's main bundle. @@ -49,54 +45,31 @@ interface RouterProps { } const AppRoot: React.FunctionComponent = React.memo( - ({ - history, - store, - coreStart: { http, notifications, uiSettings, application }, - depsStart: { data }, - }) => { - const isDarkMode = useObservable(uiSettings.get$('theme:darkMode')); - + ({ history, store, coreStart, depsStart }) => { return ( - - - - - - - - - ( -

- -

- )} - /> - - - - - ( - - )} - /> -
-
-
-
-
-
-
+ + + + ( +

+ +

+ )} + /> + + + + + ( + + )} + /> +
+
); } ); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/mocks/app_context_render.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/mocks/app_context_render.tsx new file mode 100644 index 0000000000000..af34205e2310f --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/mocks/app_context_render.tsx @@ -0,0 +1,70 @@ +/* + * 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 { createMemoryHistory } from 'history'; +import { render as reactRender, RenderOptions, RenderResult } from '@testing-library/react'; +import { appStoreFactory } from '../store'; +import { coreMock } from '../../../../../../../src/core/public/mocks'; +import { EndpointPluginStartDependencies } from '../../../plugin'; +import { depsStartMock } from './dependencies_start_mock'; +import { AppRootProvider } from '../view/app_root_provider'; + +type UiRender = (ui: React.ReactElement, options?: RenderOptions) => RenderResult; + +/** + * Mocked app root context renderer + */ +interface AppContextTestRender { + store: ReturnType; + history: ReturnType; + coreStart: ReturnType; + depsStart: EndpointPluginStartDependencies; + /** + * A wrapper around `AppRootContext` component. Uses the mocked modules as input to the + * `AppRootContext` + */ + AppWrapper: React.FC; + /** + * Renders the given UI within the created `AppWrapper` providing the given UI a mocked + * endpoint runtime context environment + */ + render: UiRender; +} + +/** + * Creates a mocked endpoint app context custom renderer that can be used to render + * component that depend upon the application's surrounding context providers. + * Factory also returns the content that was used to create the custom renderer, allowing + * for further customization. + */ +export const createAppRootMockRenderer = (): AppContextTestRender => { + const history = createMemoryHistory(); + const coreStart = coreMock.createStart({ basePath: '/mock' }); + const depsStart = depsStartMock(); + const store = appStoreFactory({ coreStart, depsStart }); + const AppWrapper: React.FunctionComponent<{ children: React.ReactElement }> = ({ children }) => ( + + {children} + + ); + const render: UiRender = (ui, options) => { + // @ts-ignore + return reactRender(ui, { + wrapper: AppWrapper, + ...options, + }); + }; + + return { + store, + history, + coreStart, + depsStart, + AppWrapper, + render, + }; +}; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/mocks.ts b/x-pack/plugins/endpoint/public/applications/endpoint/mocks/dependencies_start_mock.ts similarity index 96% rename from x-pack/plugins/endpoint/public/applications/endpoint/mocks.ts rename to x-pack/plugins/endpoint/public/applications/endpoint/mocks/dependencies_start_mock.ts index e1a90b4a416dc..00cf0bca57e66 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/mocks.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/mocks/dependencies_start_mock.ts @@ -7,7 +7,7 @@ import { dataPluginMock, Start as DataPublicStartMock, -} from '../../../../../../src/plugins/data/public/mocks'; +} from '../../../../../../../src/plugins/data/public/mocks'; type DataMock = Omit & { indexPatterns: Omit & { diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/mocks/index.ts b/x-pack/plugins/endpoint/public/applications/endpoint/mocks/index.ts new file mode 100644 index 0000000000000..65e78f27943ba --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/mocks/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 './dependencies_start_mock'; +export * from './app_context_render'; diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts index dee35aa3b895a..4dafa68ddb647 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/action.ts @@ -5,7 +5,7 @@ */ import { HostListPagination, ServerApiError } from '../../types'; -import { HostResultList, HostMetadata } from '../../../../../common/types'; +import { HostResultList, HostInfo } from '../../../../../common/types'; interface ServerReturnedHostList { type: 'serverReturnedHostList'; @@ -14,7 +14,7 @@ interface ServerReturnedHostList { interface ServerReturnedHostDetails { type: 'serverReturnedHostDetails'; - payload: HostMetadata; + payload: HostInfo; } interface ServerFailedToReturnHostDetails { diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.test.ts index 9aff66cdfb75e..6148934343635 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.test.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/index.test.ts @@ -52,7 +52,7 @@ describe('HostList store concerns', () => { }); const currentState = store.getState(); - expect(currentState.hosts).toEqual(payload.hosts); + expect(currentState.hosts).toEqual(payload.hosts.map(hostInfo => hostInfo.metadata)); expect(currentState.pageSize).toEqual(payload.request_page_size); expect(currentState.pageIndex).toEqual(payload.request_page_index); expect(currentState.total).toEqual(payload.total); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.test.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.test.ts index a1973a38b6534..8c8578426aa29 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.test.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/middleware.test.ts @@ -58,6 +58,6 @@ describe('host list middleware', () => { paging_properties: [{ page_index: 0 }, { page_size: 10 }], }), }); - expect(listData(getState())).toEqual(apiResponse.hosts); + expect(listData(getState())).toEqual(apiResponse.hosts.map(hostInfo => hostInfo.metadata)); }); }); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/mock_host_result_list.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/mock_host_result_list.ts index db39ecf448312..d4c2602e34387 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/mock_host_result_list.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/mock_host_result_list.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { HostResultList } from '../../../../../common/types'; +import { HostResultList, HostStatus } from '../../../../../common/types'; import { EndpointDocGenerator } from '../../../../../common/generate_data'; export const mockHostResultList: (options?: { @@ -27,7 +27,10 @@ export const mockHostResultList: (options?: { const hosts = []; for (let index = 0; index < actualCountToReturn; index++) { const generator = new EndpointDocGenerator('seed'); - hosts.push(generator.generateHostMetadata()); + hosts.push({ + metadata: generator.generateHostMetadata(), + host_status: HostStatus.ERROR, + }); } const mock: HostResultList = { hosts, diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/reducer.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/reducer.ts index fd70317a9f37e..ad6741dab7be7 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/reducer.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/hosts/reducer.ts @@ -34,7 +34,7 @@ export const hostListReducer: Reducer = ( } = action.payload; return { ...state, - hosts, + hosts: hosts.map(hostInfo => hostInfo.metadata), total, pageSize, pageIndex, @@ -43,7 +43,7 @@ export const hostListReducer: Reducer = ( } else if (action.type === 'serverReturnedHostDetails') { return { ...state, - details: action.payload, + details: action.payload.metadata, }; } else if (action.type === 'serverFailedToReturnHostDetails') { return { diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_details/reducer.ts b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_details/reducer.ts index af9b49cea18ad..fb3e26157ef32 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_details/reducer.ts +++ b/x-pack/plugins/endpoint/public/applications/endpoint/store/policy_details/reducer.ts @@ -74,8 +74,12 @@ export const policyDetailsReducer: Reducer = ( ...state, location: action.payload, }; + const isCurrentlyOnDetailsPage = isOnPolicyDetailsPage(newState); + const wasPreviouslyOnDetailsPage = isOnPolicyDetailsPage(state); - if (isOnPolicyDetailsPage(newState)) { + // Did user just enter the Detail page? if so, then set the loading indicator and return new state + if (isCurrentlyOnDetailsPage && !wasPreviouslyOnDetailsPage) { + newState.isLoading = true; return newState; } return { diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/app_root_provider.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/app_root_provider.tsx new file mode 100644 index 0000000000000..ca27831ee90b5 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/app_root_provider.tsx @@ -0,0 +1,58 @@ +/* + * 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, { memo, ReactNode, useMemo } from 'react'; +import { Provider } from 'react-redux'; +import { I18nProvider } from '@kbn/i18n/react'; +import { Router } from 'react-router-dom'; +import { History } from 'history'; +import { CoreStart } from 'kibana/public'; +import { useObservable } from 'react-use'; +import { EuiThemeProvider } from '../../../../../../legacy/common/eui_styled_components'; +import { KibanaContextProvider } from '../../../../../../../src/plugins/kibana_react/public'; +import { appStoreFactory } from '../store'; +import { RouteCapture } from './route_capture'; +import { EndpointPluginStartDependencies } from '../../../plugin'; + +/** + * Provides the context for rendering the endpoint app + */ +export const AppRootProvider = memo<{ + store: ReturnType; + history: History; + coreStart: CoreStart; + depsStart: EndpointPluginStartDependencies; + children: ReactNode | ReactNode[]; +}>( + ({ + store, + history, + coreStart: { http, notifications, uiSettings, application }, + depsStart: { data }, + children, + }) => { + const isDarkMode = useObservable(uiSettings.get$('theme:darkMode')); + const services = useMemo(() => ({ http, notifications, application, data }), [ + application, + data, + http, + notifications, + ]); + return ( + + + + + + {children} + + + + + + ); + } +); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/agents_summary.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/agents_summary.tsx index d0751cf9fb886..a3e30eb891db4 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/agents_summary.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/agents_summary.tsx @@ -61,7 +61,7 @@ export const AgentsSummary = memo(props => { }, []); return ( - + {stats.map(({ key, title, health }) => { return ( diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_details.test.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_details.test.tsx new file mode 100644 index 0000000000000..2ecc2b117bf01 --- /dev/null +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_details.test.tsx @@ -0,0 +1,239 @@ +/* + * 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 { mount } from 'enzyme'; +import { createAppRootMockRenderer } from '../../mocks'; +import { PolicyDetails } from './policy_details'; +import { EndpointDocGenerator } from '../../../../../common/generate_data'; + +describe('Policy Details', () => { + type FindReactWrapperResponse = ReturnType['find']>; + + const sleep = (ms = 100) => new Promise(wakeup => setTimeout(wakeup, ms)); + const generator = new EndpointDocGenerator(); + const { history, AppWrapper, coreStart } = createAppRootMockRenderer(); + const http = coreStart.http; + const render = (ui: Parameters[0]) => mount(ui, { wrappingComponent: AppWrapper }); + let policyDatasource: ReturnType; + let policyView: ReturnType; + + beforeEach(() => jest.clearAllMocks()); + + afterEach(() => { + if (policyView) { + policyView.unmount(); + } + }); + + describe('when displayed with invalid id', () => { + beforeEach(() => { + http.get.mockReturnValue(Promise.reject(new Error('policy not found'))); + history.push('/policy/1'); + policyView = render(); + }); + + it('should show loader followed by error message', () => { + expect(policyView.find('EuiLoadingSpinner').length).toBe(1); + policyView.update(); + const callout = policyView.find('EuiCallOut'); + expect(callout).toHaveLength(1); + expect(callout.prop('color')).toEqual('danger'); + expect(callout.text()).toEqual('policy not found'); + }); + }); + describe('when displayed with valid id', () => { + let asyncActions: Promise = Promise.resolve(); + + beforeEach(() => { + policyDatasource = generator.generatePolicyDatasource(); + policyDatasource.id = '1'; + + http.get.mockImplementation((...args) => { + const [path] = args; + if (typeof path === 'string') { + // GET datasouce + if (path === '/api/ingest_manager/datasources/1') { + asyncActions = asyncActions.then(async (): Promise => await sleep()); + return Promise.resolve({ + item: policyDatasource, + success: true, + }); + } + + // GET Agent status for agent config + if (path === '/api/ingest_manager/fleet/agent-status') { + asyncActions = asyncActions.then(async () => await sleep()); + return Promise.resolve({ + results: { events: 0, total: 5, online: 3, error: 1, offline: 1 }, + success: true, + }); + } + } + + return Promise.reject(new Error('unknown API call!')); + }); + history.push('/policy/1'); + policyView = render(); + }); + + it('should display back to list button and policy title', () => { + policyView.update(); + const pageHeaderLeft = policyView.find( + 'EuiPageHeaderSection[data-test-subj="pageViewHeaderLeft"]' + ); + + const backToListButton = pageHeaderLeft.find('EuiButtonEmpty'); + expect(backToListButton.prop('iconType')).toBe('arrowLeft'); + expect(backToListButton.prop('href')).toBe('/mock/app/endpoint/policy'); + expect(backToListButton.text()).toBe('Back to policy list'); + + const pageTitle = pageHeaderLeft.find('[data-test-subj="pageViewHeaderLeftTitle"]'); + expect(pageTitle).toHaveLength(1); + expect(pageTitle.text()).toEqual(policyDatasource.name); + }); + it('should navigate to list if back to link is clicked', async () => { + policyView.update(); + const backToListButton = policyView.find( + 'EuiPageHeaderSection[data-test-subj="pageViewHeaderLeft"] EuiButtonEmpty' + ); + expect(history.location.pathname).toEqual('/policy/1'); + backToListButton.simulate('click'); + expect(history.location.pathname).toEqual('/policy'); + }); + it('should display agent stats', async () => { + await asyncActions; + policyView.update(); + const headerRight = policyView.find( + 'EuiPageHeaderSection[data-test-subj="pageViewHeaderRight"]' + ); + const agentsSummary = headerRight.find('EuiFlexGroup[data-test-subj="policyAgentsSummary"]'); + expect(agentsSummary).toHaveLength(1); + expect(agentsSummary.text()).toBe('Hosts5Online3Offline1Error1'); + }); + it('should display cancel button', async () => { + await asyncActions; + policyView.update(); + const cancelbutton = policyView.find( + 'EuiButtonEmpty[data-test-subj="policyDetailsCancelButton"]' + ); + expect(cancelbutton).toHaveLength(1); + expect(cancelbutton.text()).toEqual('Cancel'); + }); + it('should redirect to policy list when cancel button is clicked', async () => { + await asyncActions; + policyView.update(); + const cancelbutton = policyView.find( + 'EuiButtonEmpty[data-test-subj="policyDetailsCancelButton"]' + ); + expect(history.location.pathname).toEqual('/policy/1'); + cancelbutton.simulate('click'); + expect(history.location.pathname).toEqual('/policy'); + }); + it('should display save button', async () => { + await asyncActions; + policyView.update(); + const saveButton = policyView.find('EuiButton[data-test-subj="policyDetailsSaveButton"]'); + expect(saveButton).toHaveLength(1); + expect(saveButton.text()).toEqual('Save'); + }); + describe('when the save button is clicked', () => { + let saveButton: FindReactWrapperResponse; + let confirmModal: FindReactWrapperResponse; + let modalCancelButton: FindReactWrapperResponse; + let modalConfirmButton: FindReactWrapperResponse; + + beforeEach(async () => { + await asyncActions; + policyView.update(); + saveButton = policyView.find('EuiButton[data-test-subj="policyDetailsSaveButton"]'); + saveButton.simulate('click'); + policyView.update(); + confirmModal = policyView.find( + 'EuiConfirmModal[data-test-subj="policyDetailsConfirmModal"]' + ); + modalCancelButton = confirmModal.find('button[data-test-subj="confirmModalCancelButton"]'); + modalConfirmButton = confirmModal.find( + 'button[data-test-subj="confirmModalConfirmButton"]' + ); + http.put.mockImplementation((...args) => { + asyncActions = asyncActions.then(async () => await sleep()); + const [path] = args; + if (typeof path === 'string') { + if (path === '/api/ingest_manager/datasources/1') { + return Promise.resolve({ + item: policyDatasource, + success: true, + }); + } + } + + return Promise.reject(new Error('unknown PUT path!')); + }); + }); + + it('should show a modal confirmation', () => { + expect(confirmModal).toHaveLength(1); + expect(confirmModal.find('div[data-test-subj="confirmModalTitleText"]').text()).toEqual( + 'Save and deploy changes' + ); + expect(modalCancelButton.text()).toEqual('Cancel'); + expect(modalConfirmButton.text()).toEqual('Save and deploy changes'); + }); + it('should show info callout if policy is in use', () => { + const warningCallout = confirmModal.find( + 'EuiCallOut[data-test-subj="policyDetailsWarningCallout"]' + ); + expect(warningCallout).toHaveLength(1); + expect(warningCallout.text()).toEqual( + 'This action will update 5 hostsSaving these changes will apply the updates to all active endpoints assigned to this policy' + ); + }); + it('should close dialog if cancel button is clicked', () => { + modalCancelButton.simulate('click'); + expect( + policyView.find('EuiConfirmModal[data-test-subj="policyDetailsConfirmModal"]') + ).toHaveLength(0); + }); + it('should update policy and show success notification when confirm button is clicked', async () => { + modalConfirmButton.simulate('click'); + policyView.update(); + // Modal should be closed + expect( + policyView.find('EuiConfirmModal[data-test-subj="policyDetailsConfirmModal"]') + ).toHaveLength(0); + + // API should be called + await asyncActions; + expect(http.put.mock.calls[0][0]).toEqual(`/api/ingest_manager/datasources/1`); + policyView.update(); + + // Toast notification should be shown + const toastAddMock = coreStart.notifications.toasts.add.mock; + expect(toastAddMock.calls).toHaveLength(1); + expect(toastAddMock.calls[0][0]).toMatchObject({ + color: 'success', + iconType: 'check', + }); + }); + it('should show an error notification toast if update fails', async () => { + policyDatasource.id = 'invalid'; + modalConfirmButton.simulate('click'); + + await asyncActions; + policyView.update(); + + // Toast notification should be shown + const toastAddMock = coreStart.notifications.toasts.add.mock; + expect(toastAddMock.calls).toHaveLength(1); + expect(toastAddMock.calls[0][0]).toMatchObject({ + color: 'danger', + iconType: 'alert', + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_details.tsx b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_details.tsx index 2dba301bf4537..bc56e5e6f6329 100644 --- a/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_details.tsx +++ b/x-pack/plugins/endpoint/public/applications/endpoint/view/policy/policy_details.tsx @@ -149,7 +149,10 @@ export const PolicyDetails = React.memo(() => { - + @@ -157,6 +160,7 @@ export const PolicyDetails = React.memo(() => { 0 && ( <> { +): Promise { const query = getESQueryHostMetadataByID(id); const response = (await context.core.elasticsearch.dataClient.callAsCurrentUser( 'search', @@ -98,7 +98,7 @@ export async function getHostData( return undefined; } - return response.hits.hits[0]._source; + return enrichHostMetadata(response.hits.hits[0]._source); } function mapToHostResultList( @@ -113,7 +113,7 @@ function mapToHostResultList( hosts: searchResponse.hits.hits .map(response => response.inner_hits.most_recent.hits.hits) .flatMap(data => data as HitSource) - .map(entry => entry._source), + .map(entry => enrichHostMetadata(entry._source)), total: totalNumberOfHosts, }; } else { @@ -125,3 +125,10 @@ function mapToHostResultList( }; } } + +function enrichHostMetadata(hostMetadata: HostMetadata): HostInfo { + return { + metadata: hostMetadata, + host_status: HostStatus.ERROR, + }; +} diff --git a/x-pack/plugins/endpoint/server/routes/metadata/metadata.test.ts b/x-pack/plugins/endpoint/server/routes/metadata/metadata.test.ts index e0fd11e737e7d..9bd251735cc04 100644 --- a/x-pack/plugins/endpoint/server/routes/metadata/metadata.test.ts +++ b/x-pack/plugins/endpoint/server/routes/metadata/metadata.test.ts @@ -18,7 +18,7 @@ import { httpServiceMock, loggingServiceMock, } from '../../../../../../src/core/server/mocks'; -import { HostMetadata, HostResultList } from '../../../common/types'; +import { HostInfo, HostMetadata, HostResultList, HostStatus } from '../../../common/types'; import { SearchResponse } from 'elasticsearch'; import { EndpointConfigSchema } from '../../config'; import * as data from '../../test_data/all_metadata_data.json'; @@ -230,7 +230,7 @@ describe('test endpoint route', () => { expect(message).toEqual('Endpoint Not Found'); }); - it('should return a single endpoint', async () => { + it('should return a single endpoint with status error', async () => { const mockRequest = httpServerMock.createKibanaRequest({ params: { id: (data as any).hits.hits[0]._id }, }); @@ -257,8 +257,9 @@ describe('test endpoint route', () => { expect(mockScopedClient.callAsCurrentUser).toBeCalled(); expect(routeConfig.options).toEqual({ authRequired: true }); expect(mockResponse.ok).toBeCalled(); - const result = mockResponse.ok.mock.calls[0][0]?.body as HostMetadata; - expect(result).toHaveProperty('endpoint'); + const result = mockResponse.ok.mock.calls[0][0]?.body as HostInfo; + expect(result).toHaveProperty('metadata.endpoint'); + expect(result.host_status).toEqual(HostStatus.ERROR); }); }); }); diff --git a/x-pack/plugins/features/common/feature.ts b/x-pack/plugins/features/common/feature.ts index 82fcc33f5c8ce..ef32a8a80a0bd 100644 --- a/x-pack/plugins/features/common/feature.ts +++ b/x-pack/plugins/features/common/feature.ts @@ -7,6 +7,7 @@ import { RecursiveReadonly } from '@kbn/utility-types'; import { FeatureKibanaPrivileges } from './feature_kibana_privileges'; import { SubFeatureConfig, SubFeature } from './sub_feature'; +import { ReservedKibanaPrivilege } from './reserved_kibana_privilege'; /** * Interface for registering a feature. @@ -122,8 +123,8 @@ export interface FeatureConfig { * @private */ reserved?: { - privilege: FeatureKibanaPrivileges; description: string; + privileges: ReservedKibanaPrivilege[]; }; } diff --git a/x-pack/plugins/features/common/reserved_kibana_privilege.ts b/x-pack/plugins/features/common/reserved_kibana_privilege.ts new file mode 100644 index 0000000000000..0186011382e84 --- /dev/null +++ b/x-pack/plugins/features/common/reserved_kibana_privilege.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 { FeatureKibanaPrivileges } from '.'; + +export interface ReservedKibanaPrivilege { + id: string; + privilege: FeatureKibanaPrivileges; +} diff --git a/x-pack/plugins/features/server/feature_registry.test.ts b/x-pack/plugins/features/server/feature_registry.test.ts index 5b4f7728c9f31..2039f8f6acda2 100644 --- a/x-pack/plugins/features/server/feature_registry.test.ts +++ b/x-pack/plugins/features/server/feature_registry.test.ts @@ -110,19 +110,24 @@ describe('FeatureRegistry', () => { ], privilegesTooltip: 'some fancy tooltip', reserved: { - privilege: { - catalogue: ['foo'], - management: { - foo: ['bar'], - }, - app: ['app1'], - savedObject: { - all: ['space', 'etc', 'telemetry'], - read: ['canvas', 'config', 'url'], + privileges: [ + { + id: 'reserved', + privilege: { + catalogue: ['foo'], + management: { + foo: ['bar'], + }, + app: ['app1'], + savedObject: { + all: ['space', 'etc', 'telemetry'], + read: ['canvas', 'config', 'url'], + }, + api: ['someApiEndpointTag', 'anotherEndpointTag'], + ui: ['allowsFoo', 'showBar', 'showBaz'], + }, }, - api: ['someApiEndpointTag', 'anotherEndpointTag'], - ui: ['allowsFoo', 'showBar', 'showBaz'], - }, + ], description: 'some completely adequate description', }, }; @@ -264,13 +269,18 @@ describe('FeatureRegistry', () => { privileges: null, reserved: { description: 'foo', - privilege: { - ui: [], - savedObject: { - all: [], - read: [], + privileges: [ + { + id: 'reserved', + privilege: { + ui: [], + savedObject: { + all: [], + read: [], + }, + }, }, - }, + ], }, }; @@ -278,7 +288,7 @@ describe('FeatureRegistry', () => { featureRegistry.register(feature); const result = featureRegistry.getAll(); - const reservedPrivilege = result[0]!.reserved!.privilege; + const reservedPrivilege = result[0]!.reserved!.privileges[0].privilege; expect(reservedPrivilege.savedObject.all).toEqual(['telemetry']); expect(reservedPrivilege.savedObject.read).toEqual(['config', 'url']); }); @@ -520,14 +530,19 @@ describe('FeatureRegistry', () => { privileges: null, reserved: { description: 'something', - privilege: { - savedObject: { - all: [], - read: [], + privileges: [ + { + id: 'reserved', + privilege: { + savedObject: { + all: [], + read: [], + }, + ui: [], + app: ['foo', 'bar', 'baz'], + }, }, - ui: [], - app: ['foo', 'bar', 'baz'], - }, + ], }, }; @@ -546,14 +561,19 @@ describe('FeatureRegistry', () => { privileges: null, reserved: { description: 'something', - privilege: { - savedObject: { - all: [], - read: [], + privileges: [ + { + id: 'reserved', + privilege: { + savedObject: { + all: [], + read: [], + }, + ui: [], + app: ['foo', 'bar'], + }, }, - ui: [], - app: ['foo', 'bar'], - }, + ], }, }; @@ -666,15 +686,20 @@ describe('FeatureRegistry', () => { privileges: null, reserved: { description: 'something', - privilege: { - catalogue: ['foo', 'bar', 'baz'], - savedObject: { - all: [], - read: [], + privileges: [ + { + id: 'reserved', + privilege: { + catalogue: ['foo', 'bar', 'baz'], + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], + }, }, - ui: [], - app: [], - }, + ], }, }; @@ -694,15 +719,20 @@ describe('FeatureRegistry', () => { privileges: null, reserved: { description: 'something', - privilege: { - catalogue: ['foo', 'bar'], - savedObject: { - all: [], - read: [], + privileges: [ + { + id: 'reserved', + privilege: { + catalogue: ['foo', 'bar'], + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], + }, }, - ui: [], - app: [], - }, + ], }, }; @@ -840,18 +870,23 @@ describe('FeatureRegistry', () => { privileges: null, reserved: { description: 'something', - privilege: { - catalogue: ['bar'], - management: { - kibana: ['hey-there'], - }, - savedObject: { - all: [], - read: [], + privileges: [ + { + id: 'reserved', + privilege: { + catalogue: ['bar'], + management: { + kibana: ['hey-there'], + }, + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], + }, }, - ui: [], - app: [], - }, + ], }, }; @@ -874,18 +909,23 @@ describe('FeatureRegistry', () => { privileges: null, reserved: { description: 'something', - privilege: { - catalogue: ['bar'], - management: { - kibana: ['hey-there'], - }, - savedObject: { - all: [], - read: [], + privileges: [ + { + id: 'reserved', + privilege: { + catalogue: ['bar'], + management: { + kibana: ['hey-there'], + }, + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], + }, }, - ui: [], - app: [], - }, + ], }, }; @@ -896,6 +936,78 @@ describe('FeatureRegistry', () => { ); }); + it('allows multiple reserved feature privileges to be registered', () => { + const feature: FeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + privileges: null, + reserved: { + description: 'my reserved privileges', + privileges: [ + { + id: 'a_reserved_1', + privilege: { + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], + }, + }, + { + id: 'a_reserved_2', + privilege: { + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], + }, + }, + ], + }, + }; + + const featureRegistry = new FeatureRegistry(); + featureRegistry.register(feature); + const result = featureRegistry.getAll(); + expect(result).toHaveLength(1); + expect(result[0].reserved?.privileges).toHaveLength(2); + }); + + it('does not allow reserved privilege ids to start with "reserved_"', () => { + const feature: FeatureConfig = { + id: 'test-feature', + name: 'Test Feature', + app: [], + privileges: null, + reserved: { + description: 'my reserved privileges', + privileges: [ + { + id: 'reserved_1', + privilege: { + savedObject: { + all: [], + read: [], + }, + ui: [], + app: [], + }, + }, + ], + }, + }; + + const featureRegistry = new FeatureRegistry(); + expect(() => featureRegistry.register(feature)).toThrowErrorMatchingInlineSnapshot( + `"child \\"reserved\\" fails because [child \\"privileges\\" fails because [\\"privileges\\" at position 0 fails because [child \\"id\\" fails because [\\"id\\" with value \\"reserved_1\\" fails to match the required pattern: /^(?!reserved_)[a-zA-Z0-9_-]+$/]]]]"` + ); + }); + it('cannot register feature after getAll has been called', () => { const feature1: FeatureConfig = { id: 'test-feature', diff --git a/x-pack/plugins/features/server/feature_registry.ts b/x-pack/plugins/features/server/feature_registry.ts index 73a353cd27471..6140b7ac87ce0 100644 --- a/x-pack/plugins/features/server/feature_registry.ts +++ b/x-pack/plugins/features/server/feature_registry.ts @@ -39,9 +39,9 @@ export class FeatureRegistry { function applyAutomaticPrivilegeGrants(feature: FeatureConfig): FeatureConfig { const allPrivilege = feature.privileges?.all; const readPrivilege = feature.privileges?.read; - const reservedPrivilege = feature.reserved?.privilege; + const reservedPrivileges = (feature.reserved?.privileges ?? []).map(rp => rp.privilege); - applyAutomaticAllPrivilegeGrants(allPrivilege, reservedPrivilege); + applyAutomaticAllPrivilegeGrants(allPrivilege, ...reservedPrivileges); applyAutomaticReadPrivilegeGrants(readPrivilege); return feature; diff --git a/x-pack/plugins/features/server/feature_schema.ts b/x-pack/plugins/features/server/feature_schema.ts index fdeceb30b4e3d..403d9586bf160 100644 --- a/x-pack/plugins/features/server/feature_schema.ts +++ b/x-pack/plugins/features/server/feature_schema.ts @@ -18,6 +18,7 @@ const prohibitedFeatureIds: Array = ['catalogue', 'managem const featurePrivilegePartRegex = /^[a-zA-Z0-9_-]+$/; const subFeaturePrivilegePartRegex = /^[a-zA-Z0-9_-]+$/; const managementSectionIdRegex = /^[a-zA-Z0-9_-]+$/; +const reservedFeaturePrrivilegePartRegex = /^(?!reserved_)[a-zA-Z0-9_-]+$/; export const uiCapabilitiesRegex = /^[a-zA-Z0-9:_-]+$/; const managementSchema = Joi.object().pattern( @@ -118,8 +119,17 @@ const schema = Joi.object({ }), privilegesTooltip: Joi.string(), reserved: Joi.object({ - privilege: privilegeSchema.required(), description: Joi.string().required(), + privileges: Joi.array() + .items( + Joi.object({ + id: Joi.string() + .regex(reservedFeaturePrrivilegePartRegex) + .required(), + privilege: privilegeSchema.required(), + }) + ) + .required(), }), }); @@ -209,7 +219,9 @@ export function validateFeature(feature: FeatureConfig) { privilegeEntries.push(...Object.entries(feature.privileges)); } if (feature.reserved) { - privilegeEntries.push(['reserved', feature.reserved.privilege]); + feature.reserved.privileges.forEach(reservedPrivilege => { + privilegeEntries.push([reservedPrivilege.id, reservedPrivilege.privilege]); + }); } if (privilegeEntries.length === 0) { diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx index da4b8e6f6eef2..95630a6981843 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/load_mappings/load_mappings_provider.test.tsx @@ -51,7 +51,8 @@ const openModalWithJsonContent = ({ find, waitFor }: TestBed) => async (json: an }); }; -describe('', () => { +// FLAKY: https://github.com/elastic/kibana/issues/59030 +describe.skip('', () => { test('it should forward valid mapping definition', async () => { const mappingsToLoad = { properties: { diff --git a/x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx b/x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx index cd3ba43c3607c..2e43ede2480ce 100644 --- a/x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx +++ b/x-pack/plugins/infra/public/components/alerting/metrics/expression.tsx @@ -17,6 +17,11 @@ import { import { IFieldType } from 'src/plugins/data/public'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; +import { + MetricExpressionParams, + Comparator, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../server/lib/alerting/metric_threshold/types'; import { euiStyled } from '../../../../../observability/public'; import { WhenExpression, @@ -36,16 +41,6 @@ import { MetricsExplorerSeries } from '../../../../common/http_api/metrics_explo import { useSource } from '../../../containers/source'; import { MetricsExplorerGroupBy } from '../../metrics_explorer/group_by'; -export interface MetricExpression { - aggType?: string; - metric?: string; - comparator?: Comparator; - threshold?: number[]; - timeSize?: number; - timeUnit?: TimeUnit; - indexPattern?: string; -} - interface AlertContextMeta { currentOptions?: Partial; series?: MetricsExplorerSeries; @@ -57,14 +52,35 @@ interface Props { criteria: MetricExpression[]; groupBy?: string; filterQuery?: string; + sourceId?: string; }; alertsContext: AlertsContextValue; setAlertParams(key: string, value: any): void; setAlertProperty(key: string, value: any): void; } -type Comparator = '>' | '>=' | 'between' | '<' | '<='; type TimeUnit = 's' | 'm' | 'h' | 'd'; +type MetricExpression = Omit & { + metric?: string; +}; + +enum AGGREGATION_TYPES { + COUNT = 'count', + AVERAGE = 'avg', + SUM = 'sum', + MIN = 'min', + MAX = 'max', + RATE = 'rate', + CARDINALITY = 'cardinality', +} + +const defaultExpression = { + aggType: AGGREGATION_TYPES.AVERAGE, + comparator: Comparator.GT, + threshold: [], + timeSize: 1, + timeUnit: 'm', +} as MetricExpression; export const Expressions: React.FC = props => { const { setAlertParams, alertParams, errors, alertsContext } = props; @@ -87,18 +103,6 @@ export const Expressions: React.FC = props => { } }, [alertsContext.metadata]); - const defaultExpression = useMemo( - () => ({ - aggType: AGGREGATION_TYPES.AVERAGE, - comparator: '>', - threshold: [], - timeSize: 1, - timeUnit: 'm', - indexPattern: source?.configuration.metricAlias, - }), - [source] - ); - const updateParams = useCallback( (id, e: MetricExpression) => { const exp = alertParams.criteria ? alertParams.criteria.slice() : []; @@ -112,7 +116,7 @@ export const Expressions: React.FC = props => { const exp = alertParams.criteria.slice(); exp.push(defaultExpression); setAlertParams('criteria', exp); - }, [setAlertParams, alertParams.criteria, defaultExpression]); + }, [setAlertParams, alertParams.criteria]); const removeExpression = useCallback( (id: number) => { @@ -179,11 +183,10 @@ export const Expressions: React.FC = props => { 'criteria', md.currentOptions.metrics.map(metric => ({ metric: metric.field, - comparator: '>', + comparator: Comparator.GT, threshold: [], timeSize, timeUnit, - indexPattern: source?.configuration.metricAlias, aggType: metric.aggregation, })) ); @@ -201,6 +204,7 @@ export const Expressions: React.FC = props => { setAlertParams('groupBy', md.currentOptions.groupBy); } + setAlertParams('sourceId', source?.id); } }, [alertsContext.metadata, defaultExpression, source]); // eslint-disable-line react-hooks/exhaustive-deps @@ -316,26 +320,31 @@ interface ExpressionRowProps { const StyledExpressionRow = euiStyled(EuiFlexGroup)` display: flex; flex-wrap: wrap; - margin: 0 -${props => props.theme.eui.euiSizeXS}; + margin: 0 -4px; `; const StyledExpression = euiStyled.div` - padding: 0 ${props => props.theme.eui.euiSizeXS}; + padding: 0 4px; `; export const ExpressionRow: React.FC = props => { const { setAlertParams, expression, errors, expressionId, remove, fields, canDelete } = props; - const { aggType = AGGREGATION_TYPES.MAX, metric, comparator = '>', threshold = [] } = expression; + const { + aggType = AGGREGATION_TYPES.MAX, + metric, + comparator = Comparator.GT, + threshold = [], + } = expression; const updateAggType = useCallback( (at: string) => { - setAlertParams(expressionId, { ...expression, aggType: at }); + setAlertParams(expressionId, { ...expression, aggType: at as MetricExpression['aggType'] }); }, [expressionId, expression, setAlertParams] ); const updateMetric = useCallback( - (m?: string) => { + (m?: MetricExpression['metric']) => { setAlertParams(expressionId, { ...expression, metric: m }); }, [expressionId, expression, setAlertParams] @@ -384,7 +393,7 @@ export const ExpressionRow: React.FC = props => { )} '} + thresholdComparator={comparator || Comparator.GT} threshold={threshold} onChangeSelectedThresholdComparator={updateComparator} onChangeSelectedThreshold={updateThreshold} @@ -411,16 +420,6 @@ export const ExpressionRow: React.FC = props => { ); }; -enum AGGREGATION_TYPES { - COUNT = 'count', - AVERAGE = 'avg', - SUM = 'sum', - MIN = 'min', - MAX = 'max', - RATE = 'rate', - CARDINALITY = 'cardinality', -} - export const aggregationType: { [key: string]: any } = { avg: { text: i18n.translate('xpack.infra.metrics.alertFlyout.aggregationText.avg', { diff --git a/x-pack/plugins/infra/public/components/alerting/metrics/validation.tsx b/x-pack/plugins/infra/public/components/alerting/metrics/validation.tsx index 0f5b07f8c0e13..d84e46d08a287 100644 --- a/x-pack/plugins/infra/public/components/alerting/metrics/validation.tsx +++ b/x-pack/plugins/infra/public/components/alerting/metrics/validation.tsx @@ -6,15 +6,14 @@ import { i18n } from '@kbn/i18n'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths - -import { MetricExpression } from './expression'; +import { MetricExpressionParams } from '../../../../server/lib/alerting/metric_threshold/types'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { ValidationResult } from '../../../../../triggers_actions_ui/public/types'; export function validateMetricThreshold({ criteria, }: { - criteria: MetricExpression[]; + criteria: MetricExpressionParams[]; }): ValidationResult { const validationResult = { errors: {} }; const errors: { @@ -24,6 +23,7 @@ export function validateMetricThreshold({ timeWindowSize: string[]; threshold0: string[]; threshold1: string[]; + metric: string[]; }; } = {}; validationResult.errors = errors; @@ -42,6 +42,7 @@ export function validateMetricThreshold({ timeWindowSize: [], threshold0: [], threshold1: [], + metric: [], }; if (!c.aggType) { errors[id].aggField.push( @@ -74,6 +75,14 @@ export function validateMetricThreshold({ }) ); } + + if (!c.metric && c.aggType !== 'count') { + errors[id].metric.push( + i18n.translate('xpack.infra.metrics.alertFlyout.error.metricRequired', { + defaultMessage: 'Metric is required.', + }) + ); + } }); return validationResult; diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts index 09f1702349542..38cd0cec145f9 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts @@ -16,7 +16,8 @@ const executor = createMetricThresholdExecutor('test') as (opts: { const alertInstances = new Map(); const services = { - callCluster(_: string, { body }: any) { + callCluster(_: string, { body, index }: any) { + if (index === 'alternatebeat-*') return mocks.changedSourceIdResponse; const metric = body.query.bool.filter[1]?.exists.field; if (body.aggs.groupings) { if (body.aggs.groupings.composite.after) { @@ -55,6 +56,13 @@ const services = { }, }; }, + savedObjectsClient: { + get(_: string, sourceId: string) { + if (sourceId === 'alternate') + return { id: 'alternate', attributes: { metricAlias: 'alternatebeat-*' } }; + return { id: 'default', attributes: { metricAlias: 'metricbeat-*' } }; + }, + }, }; const baseCriterion = { @@ -62,15 +70,15 @@ const baseCriterion = { metric: 'test.metric.1', timeSize: 1, timeUnit: 'm', - indexPattern: 'metricbeat-*', }; describe('The metric threshold alert type', () => { describe('querying the entire infrastructure', () => { const instanceID = 'test-*'; - const execute = (comparator: Comparator, threshold: number[]) => + const execute = (comparator: Comparator, threshold: number[], sourceId: string = 'default') => executor({ services, params: { + sourceId, criteria: [ { ...baseCriterion, @@ -134,6 +142,14 @@ describe('The metric threshold alert type', () => { expect(mostRecentAction.action.thresholdOf.condition0).toStrictEqual([0.75]); expect(mostRecentAction.action.metricOf.condition0).toBe('test.metric.1'); }); + test('fetches the index pattern dynamically', async () => { + await execute(Comparator.LT, [17], 'alternate'); + expect(alertInstances.get(instanceID).mostRecentAction.id).toBe(FIRED_ACTIONS.id); + expect(alertInstances.get(instanceID).state.alertState).toBe(AlertStates.ALERT); + await execute(Comparator.LT, [1.5], 'alternate'); + expect(alertInstances.get(instanceID).mostRecentAction).toBe(undefined); + expect(alertInstances.get(instanceID).state.alertState).toBe(AlertStates.OK); + }); }); describe('querying with a groupBy parameter', () => { diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts index 60bba61b75ef1..c5ea65f7a4d1a 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts @@ -5,6 +5,8 @@ */ import { mapValues } from 'lodash'; import { i18n } from '@kbn/i18n'; +import { convertSavedObjectToSavedSourceConfiguration } from '../../sources/sources'; +import { infraSourceConfigurationSavedObjectType } from '../../sources/saved_object_mappings'; import { InfraDatabaseSearchResponse } from '../../adapters/framework/adapter_types'; import { createAfterKeyHandler } from '../../../utils/create_afterkey_handler'; import { getAllCompositeData } from '../../../utils/get_all_composite_data'; @@ -15,6 +17,7 @@ import { getIntervalInSeconds } from '../../../utils/get_interval_in_seconds'; import { getDateHistogramOffset } from '../../snapshot/query_helpers'; const TOTAL_BUCKETS = 5; +const DEFAULT_INDEX_PATTERN = 'metricbeat-*'; interface Aggregation { aggregatedIntervals: { @@ -165,18 +168,42 @@ export const getElasticsearchMetricQuery = ( }; }; +const getIndexPattern: ( + services: AlertServices, + sourceId?: string +) => Promise = async function({ savedObjectsClient }, sourceId = 'default') { + try { + const sourceConfiguration = await savedObjectsClient.get( + infraSourceConfigurationSavedObjectType, + sourceId + ); + const { metricAlias } = convertSavedObjectToSavedSourceConfiguration( + sourceConfiguration + ).configuration; + return metricAlias || DEFAULT_INDEX_PATTERN; + } catch (e) { + if (e.output.statusCode === 404) { + return DEFAULT_INDEX_PATTERN; + } else { + throw e; + } + } +}; + const getMetric: ( services: AlertServices, params: MetricExpressionParams, + index: string, groupBy: string | undefined, filterQuery: string | undefined ) => Promise> = async function( - { callCluster }, + { savedObjectsClient, callCluster }, params, + index, groupBy, filterQuery ) { - const { indexPattern, aggType } = params; + const { aggType } = params; const searchBody = getElasticsearchMetricQuery(params, groupBy, filterQuery); try { @@ -189,7 +216,7 @@ const getMetric: ( response => response.aggregations?.groupings?.after_key ); const compositeBuckets = (await getAllCompositeData( - body => callCluster('search', { body, index: indexPattern }), + body => callCluster('search', { body, index }), searchBody, bucketSelector, afterKeyHandler @@ -204,7 +231,7 @@ const getMetric: ( } const result = await callCluster('search', { body: searchBody, - index: indexPattern, + index, }); return { '*': getCurrentValueFromAggregations(result.aggregations, aggType) }; } catch (e) { @@ -236,16 +263,18 @@ const mapToConditionsLookup = ( export const createMetricThresholdExecutor = (alertUUID: string) => async function({ services, params }: AlertExecutorOptions) { - const { criteria, groupBy, filterQuery } = params as { + const { criteria, groupBy, filterQuery, sourceId } = params as { criteria: MetricExpressionParams[]; groupBy: string | undefined; filterQuery: string | undefined; + sourceId?: string; }; const alertResults = await Promise.all( criteria.map(criterion => (async () => { - const currentValues = await getMetric(services, criterion, groupBy, filterQuery); + const index = await getIndexPattern(services, sourceId); + const currentValues = await getMetric(services, criterion, index, groupBy, filterQuery); const { threshold, comparator } = criterion; const comparisonFunction = comparatorMap[comparator]; return mapValues(currentValues, value => ({ diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts index 2ab3a3322661d..8808219cabaa7 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts @@ -29,7 +29,6 @@ export async function registerMetricThresholdAlertType(alertingPlugin: PluginSet ]), timeUnit: schema.string(), timeSize: schema.number(), - indexPattern: schema.string(), }; const nonCountCriterion = schema.object({ @@ -89,6 +88,7 @@ export async function registerMetricThresholdAlertType(alertingPlugin: PluginSet criteria: schema.arrayOf(schema.oneOf([countCriterion, nonCountCriterion])), groupBy: schema.maybe(schema.string()), filterQuery: schema.maybe(schema.string()), + sourceId: schema.string(), }), }, defaultActionGroupId: FIRED_ACTIONS.id, diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/test_mocks.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/test_mocks.ts index e87ffcfb2b912..66e0a363c8983 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/test_mocks.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/test_mocks.ts @@ -26,6 +26,17 @@ const bucketsB = [ }, ]; +const bucketsC = [ + { + doc_count: 2, + aggregatedValue: { value: 0.5 }, + }, + { + doc_count: 3, + aggregatedValue: { value: 16.0 }, + }, +]; + export const basicMetricResponse = { aggregations: { aggregatedIntervals: { @@ -108,3 +119,11 @@ export const compositeEndResponse = { aggregations: {}, hits: { total: { value: 0 } }, }; + +export const changedSourceIdResponse = { + aggregations: { + aggregatedIntervals: { + buckets: bucketsC, + }, + }, +}; diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/types.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/types.ts index 557a071ec9175..abed691f109c0 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/types.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/types.ts @@ -28,7 +28,7 @@ export type TimeUnit = 's' | 'm' | 'h' | 'd'; interface BaseMetricExpressionParams { timeSize: number; timeUnit: TimeUnit; - indexPattern: string; + sourceId?: string; threshold: number[]; comparator: Comparator; } diff --git a/x-pack/plugins/infra/server/lib/sources/sources.ts b/x-pack/plugins/infra/server/lib/sources/sources.ts index 5b9207a88e6e4..c7ff6c9638204 100644 --- a/x-pack/plugins/infra/server/lib/sources/sources.ts +++ b/x-pack/plugins/infra/server/lib/sources/sources.ts @@ -231,7 +231,7 @@ const mergeSourceConfiguration = ( first ); -const convertSavedObjectToSavedSourceConfiguration = (savedObject: unknown) => +export const convertSavedObjectToSavedSourceConfiguration = (savedObject: unknown) => pipe( SourceConfigurationSavedObjectRuntimeType.decode(savedObject), map(savedSourceConfiguration => ({ diff --git a/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.test.ts b/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.test.ts index ab039be8e7c22..b897c03e89f82 100644 --- a/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.test.ts +++ b/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.test.ts @@ -41,7 +41,7 @@ describe('Ingest Manager - storedDatasourceToAgentDatasource', () => { }, { id: 'test-logs-bar', - enabled: false, + enabled: true, dataset: 'bar', config: { barVar: { value: 'bar-value' }, @@ -119,7 +119,7 @@ describe('Ingest Manager - storedDatasourceToAgentDatasource', () => { }, { id: 'test-logs-bar', - enabled: false, + enabled: true, dataset: 'bar', barVar: 'bar-value', barVar2: [1, 2], @@ -140,6 +140,44 @@ describe('Ingest Manager - storedDatasourceToAgentDatasource', () => { }); }); + it('returns agent datasource config without disabled streams', () => { + expect( + storedDatasourceToAgentDatasource({ + ...mockDatasource, + inputs: [ + { + ...mockInput, + streams: [{ ...mockInput.streams[0] }, { ...mockInput.streams[1], enabled: false }], + }, + ], + }) + ).toEqual({ + id: 'mock-datasource', + namespace: 'default', + enabled: true, + use_output: 'default', + inputs: [ + { + type: 'test-logs', + enabled: true, + inputVar: 'input-value', + inputVar3: { + testField: 'test', + }, + streams: [ + { + id: 'test-logs-foo', + enabled: true, + dataset: 'foo', + fooVar: 'foo-value', + fooVar2: [1, 2], + }, + ], + }, + ], + }); + }); + it('returns agent datasource config without disabled inputs', () => { expect( storedDatasourceToAgentDatasource({ diff --git a/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.ts b/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.ts index f58eaacb7be67..20bbbec8919d6 100644 --- a/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.ts +++ b/x-pack/plugins/ingest_manager/common/services/datasource_to_agent_datasource.ts @@ -51,14 +51,16 @@ export const storedDatasourceToAgentDatasource = ( const fullInput = { ...input, ...Object.entries(input.config || {}).reduce(configReducer, {}), - streams: input.streams.map(stream => { - const fullStream = { - ...stream, - ...Object.entries(stream.config || {}).reduce(configReducer, {}), - }; - delete fullStream.config; - return fullStream; - }), + streams: input.streams + .filter(stream => stream.enabled) + .map(stream => { + const fullStream = { + ...stream, + ...Object.entries(stream.config || {}).reduce(configReducer, {}), + }; + delete fullStream.config; + return fullStream; + }), }; delete fullInput.config; return fullInput; diff --git a/x-pack/plugins/ingest_manager/common/services/package_to_config.test.ts b/x-pack/plugins/ingest_manager/common/services/package_to_config.test.ts index 357f407811880..5fa7af2dda79a 100644 --- a/x-pack/plugins/ingest_manager/common/services/package_to_config.test.ts +++ b/x-pack/plugins/ingest_manager/common/services/package_to_config.test.ts @@ -24,6 +24,7 @@ describe('Ingest Manager - packageToConfig', () => { visualization: [], search: [], 'index-pattern': [], + map: [], }, }, status: InstallationStatus.notInstalled, diff --git a/x-pack/plugins/ingest_manager/common/types/models/epm.ts b/x-pack/plugins/ingest_manager/common/types/models/epm.ts index 28786530db018..efa6621001038 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/epm.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/epm.ts @@ -28,6 +28,7 @@ export enum KibanaAssetType { visualization = 'visualization', search = 'search', indexPattern = 'index-pattern', + map = 'map', } export enum ElasticsearchAssetType { diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_config.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_config.tsx index 356739af1ff9a..0e8763cb2d4c0 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_config.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_config.tsx @@ -6,26 +6,38 @@ import React, { useState, Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { - EuiFlexGrid, EuiFlexGroup, EuiFlexItem, EuiText, + EuiTextColor, EuiSpacer, EuiButtonEmpty, EuiTitle, + EuiIconTip, } from '@elastic/eui'; import { DatasourceInput, RegistryVarsEntry } from '../../../../types'; -import { isAdvancedVar } from '../services'; +import { isAdvancedVar, DatasourceConfigValidationResults, validationHasErrors } from '../services'; import { DatasourceInputVarField } from './datasource_input_var_field'; export const DatasourceInputConfig: React.FunctionComponent<{ packageInputVars?: RegistryVarsEntry[]; datasourceInput: DatasourceInput; updateDatasourceInput: (updatedInput: Partial) => void; -}> = ({ packageInputVars, datasourceInput, updateDatasourceInput }) => { + inputVarsValidationResults: DatasourceConfigValidationResults; + forceShowErrors?: boolean; +}> = ({ + packageInputVars, + datasourceInput, + updateDatasourceInput, + inputVarsValidationResults, + forceShowErrors, +}) => { // Showing advanced options toggle state const [isShowingAdvanced, setIsShowingAdvanced] = useState(false); + // Errors state + const hasErrors = forceShowErrors && validationHasErrors(inputVarsValidationResults); + const requiredVars: RegistryVarsEntry[] = []; const advancedVars: RegistryVarsEntry[] = []; @@ -40,15 +52,36 @@ export const DatasourceInputConfig: React.FunctionComponent<{ } return ( - - + + -

- -

+ + +

+ + + +

+
+ {hasErrors ? ( + + + } + position="right" + type="alert" + iconProps={{ color: 'danger' }} + /> + + ) : null} +
@@ -60,7 +93,7 @@ export const DatasourceInputConfig: React.FunctionComponent<{

- + {requiredVars.map(varDef => { const { name: varName, type: varType } = varDef; @@ -81,6 +114,8 @@ export const DatasourceInputConfig: React.FunctionComponent<{ }, }); }} + errors={inputVarsValidationResults.config![varName]} + forceShowErrors={forceShowErrors} /> ); @@ -123,6 +158,8 @@ export const DatasourceInputConfig: React.FunctionComponent<{ }, }); }} + errors={inputVarsValidationResults.config![varName]} + forceShowErrors={forceShowErrors} /> ); @@ -132,6 +169,6 @@ export const DatasourceInputConfig: React.FunctionComponent<{ ) : null}
-
+
); }; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_panel.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_panel.tsx index 74b08f48df12d..6b0c68ccb7d3f 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_panel.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_panel.tsx @@ -17,8 +17,10 @@ import { EuiButtonIcon, EuiHorizontalRule, EuiSpacer, + EuiIconTip, } from '@elastic/eui'; import { DatasourceInput, DatasourceInputStream, RegistryInput } from '../../../../types'; +import { DatasourceInputValidationResults, validationHasErrors } from '../services'; import { DatasourceInputConfig } from './datasource_input_config'; import { DatasourceInputStreamConfig } from './datasource_input_stream_config'; @@ -32,10 +34,21 @@ export const DatasourceInputPanel: React.FunctionComponent<{ packageInput: RegistryInput; datasourceInput: DatasourceInput; updateDatasourceInput: (updatedInput: Partial) => void; -}> = ({ packageInput, datasourceInput, updateDatasourceInput }) => { + inputValidationResults: DatasourceInputValidationResults; + forceShowErrors?: boolean; +}> = ({ + packageInput, + datasourceInput, + updateDatasourceInput, + inputValidationResults, + forceShowErrors, +}) => { // Showing streams toggle state const [isShowingStreams, setIsShowingStreams] = useState(false); + // Errors state + const hasErrors = forceShowErrors && validationHasErrors(inputValidationResults); + return ( {/* Header / input-level toggle */} @@ -43,9 +56,32 @@ export const DatasourceInputPanel: React.FunctionComponent<{ -

{packageInput.title || packageInput.type}

- + + + +

+ + {packageInput.title || packageInput.type} + +

+
+
+ {hasErrors ? ( + + + } + position="right" + type="alert" + iconProps={{ color: 'danger' }} + /> + + ) : null} +
} checked={datasourceInput.enabled} onChange={e => { @@ -122,6 +158,8 @@ export const DatasourceInputPanel: React.FunctionComponent<{ packageInputVars={packageInput.vars} datasourceInput={datasourceInput} updateDatasourceInput={updateDatasourceInput} + inputVarsValidationResults={{ config: inputValidationResults.config }} + forceShowErrors={forceShowErrors} /> @@ -165,6 +203,10 @@ export const DatasourceInputPanel: React.FunctionComponent<{ updateDatasourceInput(updatedInput); }} + inputStreamValidationResults={ + inputValidationResults.streams![datasourceInputStream.id] + } + forceShowErrors={forceShowErrors} /> diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_stream_config.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_stream_config.tsx index 3bf5b2bb4c0f0..43e8f5a2c060d 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_stream_config.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_stream_config.tsx @@ -7,26 +7,38 @@ import React, { useState, Fragment } from 'react'; import ReactMarkdown from 'react-markdown'; import { FormattedMessage } from '@kbn/i18n/react'; import { - EuiFlexGrid, EuiFlexGroup, EuiFlexItem, EuiSwitch, EuiText, EuiSpacer, EuiButtonEmpty, + EuiTextColor, + EuiIconTip, } from '@elastic/eui'; import { DatasourceInputStream, RegistryStream, RegistryVarsEntry } from '../../../../types'; -import { isAdvancedVar } from '../services'; +import { isAdvancedVar, DatasourceConfigValidationResults, validationHasErrors } from '../services'; import { DatasourceInputVarField } from './datasource_input_var_field'; export const DatasourceInputStreamConfig: React.FunctionComponent<{ packageInputStream: RegistryStream; datasourceInputStream: DatasourceInputStream; updateDatasourceInputStream: (updatedStream: Partial) => void; -}> = ({ packageInputStream, datasourceInputStream, updateDatasourceInputStream }) => { + inputStreamValidationResults: DatasourceConfigValidationResults; + forceShowErrors?: boolean; +}> = ({ + packageInputStream, + datasourceInputStream, + updateDatasourceInputStream, + inputStreamValidationResults, + forceShowErrors, +}) => { // Showing advanced options toggle state const [isShowingAdvanced, setIsShowingAdvanced] = useState(false); + // Errors state + const hasErrors = forceShowErrors && validationHasErrors(inputStreamValidationResults); + const requiredVars: RegistryVarsEntry[] = []; const advancedVars: RegistryVarsEntry[] = []; @@ -41,10 +53,33 @@ export const DatasourceInputStreamConfig: React.FunctionComponent<{ } return ( - - + + + + + {packageInputStream.title || packageInputStream.dataset} + + + {hasErrors ? ( + + + } + position="right" + type="alert" + iconProps={{ color: 'danger' }} + /> + + ) : null} + + } checked={datasourceInputStream.enabled} onChange={e => { const enabled = e.target.checked; @@ -62,7 +97,7 @@ export const DatasourceInputStreamConfig: React.FunctionComponent<{ ) : null} - + {requiredVars.map(varDef => { const { name: varName, type: varType } = varDef; @@ -83,6 +118,8 @@ export const DatasourceInputStreamConfig: React.FunctionComponent<{ }, }); }} + errors={inputStreamValidationResults.config![varName]} + forceShowErrors={forceShowErrors} /> ); @@ -125,6 +162,8 @@ export const DatasourceInputStreamConfig: React.FunctionComponent<{ }, }); }} + errors={inputStreamValidationResults.config![varName]} + forceShowErrors={forceShowErrors} /> ); @@ -134,6 +173,6 @@ export const DatasourceInputStreamConfig: React.FunctionComponent<{ ) : null}
- + ); }; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_var_field.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_var_field.tsx index bcb99eed88ac0..846a807f9240d 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_var_field.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/datasource_input_var_field.tsx @@ -3,7 +3,7 @@ * 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 React, { useState } from 'react'; import ReactMarkdown from 'react-markdown'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiFormRow, EuiFieldText, EuiComboBox, EuiText, EuiCodeEditor } from '@elastic/eui'; @@ -16,12 +16,20 @@ export const DatasourceInputVarField: React.FunctionComponent<{ varDef: RegistryVarsEntry; value: any; onChange: (newValue: any) => void; -}> = ({ varDef, value, onChange }) => { + errors?: string[] | null; + forceShowErrors?: boolean; +}> = ({ varDef, value, onChange, errors: varErrors, forceShowErrors }) => { + const [isDirty, setIsDirty] = useState(false); + const { multi, required, type, title, name, description } = varDef; + const isInvalid = (isDirty || forceShowErrors) && !!varErrors; + const errors = isInvalid ? varErrors : null; + const renderField = () => { - if (varDef.multi) { + if (multi) { return ( ({ label: val }))} onCreateOption={(newVal: any) => { onChange([...value, newVal]); @@ -29,10 +37,11 @@ export const DatasourceInputVarField: React.FunctionComponent<{ onChange={(newVals: any[]) => { onChange(newVals.map(val => val.label)); }} + onBlur={() => setIsDirty(true)} /> ); } - if (varDef.type === 'yaml') { + if (type === 'yaml') { return ( onChange(newVal)} + onBlur={() => setIsDirty(true)} /> ); } return ( onChange(e.target.value)} + onBlur={() => setIsDirty(true)} /> ); }; return ( ) : null } - helpText={} + helpText={} > {renderField()} diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/index.ts index e5f18e1449d1b..3bfca75668911 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/components/index.ts @@ -5,3 +5,4 @@ */ export { CreateDatasourcePageLayout } from './layout'; export { DatasourceInputPanel } from './datasource_input_panel'; +export { DatasourceInputVarField } from './datasource_input_var_field'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/index.tsx index 23d0f3317a667..7815ab9cd1d6e 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/index.tsx @@ -21,6 +21,7 @@ import { useLinks as useEPMLinks } from '../../epm/hooks'; import { CreateDatasourcePageLayout } from './components'; import { CreateDatasourceFrom, CreateDatasourceStep } from './types'; import { CREATE_DATASOURCE_STEP_PATHS } from './constants'; +import { DatasourceValidationResults, validateDatasource } from './services'; import { StepSelectPackage } from './step_select_package'; import { StepSelectConfig } from './step_select_config'; import { StepConfigureDatasource } from './step_configure_datasource'; @@ -51,6 +52,9 @@ export const CreateDatasourcePage: React.FunctionComponent = () => { inputs: [], }); + // Datasource validation state + const [validationResults, setValidationResults] = useState(); + // Update package info method const updatePackageInfo = (updatedPackageInfo: PackageInfo | undefined) => { if (updatedPackageInfo) { @@ -84,9 +88,18 @@ export const CreateDatasourcePage: React.FunctionComponent = () => { ...updatedFields, }; setDatasource(newDatasource); - // eslint-disable-next-line no-console console.debug('Datasource updated', newDatasource); + updateDatasourceValidation(newDatasource); + }; + + const updateDatasourceValidation = (newDatasource?: NewDatasource) => { + if (packageInfo) { + const newValidationResult = validateDatasource(newDatasource || datasource, packageInfo); + setValidationResults(newValidationResult); + // eslint-disable-next-line no-console + console.debug('Datasource validation results', newValidationResult); + } }; // Cancel url @@ -202,6 +215,7 @@ export const CreateDatasourcePage: React.FunctionComponent = () => { packageInfo={packageInfo} datasource={datasource} updateDatasource={updateDatasource} + validationResults={validationResults!} backLink={ {from === 'config' ? ( diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/index.ts index 44e5bfa41cb9b..d99f0712db3c3 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/index.ts @@ -4,3 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ export { isAdvancedVar } from './is_advanced_var'; +export { + DatasourceValidationResults, + DatasourceConfigValidationResults, + DatasourceInputValidationResults, + validateDatasource, + validationHasErrors, +} from './validate_datasource'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/validate_datasource.test.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/validate_datasource.test.ts new file mode 100644 index 0000000000000..a45fabeb5ed6a --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/validate_datasource.test.ts @@ -0,0 +1,504 @@ +/* + * 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 { + PackageInfo, + InstallationStatus, + NewDatasource, + RegistryDatasource, +} from '../../../../types'; +import { validateDatasource, validationHasErrors } from './validate_datasource'; + +describe('Ingest Manager - validateDatasource()', () => { + const mockPackage = ({ + name: 'mock-package', + title: 'Mock package', + version: '0.0.0', + description: 'description', + type: 'mock', + categories: [], + requirement: { kibana: { versions: '' }, elasticsearch: { versions: '' } }, + format_version: '', + download: '', + path: '', + assets: { + kibana: { + dashboard: [], + visualization: [], + search: [], + 'index-pattern': [], + }, + }, + status: InstallationStatus.notInstalled, + datasources: [ + { + name: 'datasource1', + title: 'Datasource 1', + description: 'test datasource', + inputs: [ + { + type: 'foo', + title: 'Foo', + vars: [ + { default: 'foo-input-var-value', name: 'foo-input-var-name', type: 'text' }, + { + default: 'foo-input2-var-value', + name: 'foo-input2-var-name', + required: true, + type: 'text', + }, + { name: 'foo-input3-var-name', type: 'text', required: true, multi: true }, + ], + streams: [ + { + dataset: 'foo', + input: 'foo', + title: 'Foo', + vars: [{ name: 'var-name', type: 'yaml' }], + }, + ], + }, + { + type: 'bar', + title: 'Bar', + vars: [ + { + default: ['value1', 'value2'], + name: 'bar-input-var-name', + type: 'text', + multi: true, + }, + { name: 'bar-input2-var-name', required: true, type: 'text' }, + ], + streams: [ + { + dataset: 'bar', + input: 'bar', + title: 'Bar', + vars: [{ name: 'var-name', type: 'yaml', required: true }], + }, + { + dataset: 'bar2', + input: 'bar2', + title: 'Bar 2', + vars: [{ default: 'bar2-var-value', name: 'var-name', type: 'text' }], + }, + ], + }, + { + type: 'with-no-config-or-streams', + title: 'With no config or streams', + streams: [], + }, + { + type: 'with-disabled-streams', + title: 'With disabled streams', + streams: [ + { + dataset: 'disabled', + input: 'disabled', + title: 'Disabled', + enabled: false, + vars: [{ multi: true, required: true, name: 'var-name', type: 'text' }], + }, + { dataset: 'disabled2', input: 'disabled2', title: 'Disabled 2', enabled: false }, + ], + }, + ], + }, + ], + } as unknown) as PackageInfo; + + const validDatasource: NewDatasource = { + name: 'datasource1-1', + config_id: 'test-config', + enabled: true, + output_id: 'test-output', + inputs: [ + { + type: 'foo', + enabled: true, + config: { + 'foo-input-var-name': { value: 'foo-input-var-value', type: 'text' }, + 'foo-input2-var-name': { value: 'foo-input2-var-value', type: 'text' }, + 'foo-input3-var-name': { value: ['test'], type: 'text' }, + }, + streams: [ + { + id: 'foo-foo', + dataset: 'foo', + enabled: true, + config: { 'var-name': { value: 'test_yaml: value', type: 'yaml' } }, + }, + ], + }, + { + type: 'bar', + enabled: true, + config: { + 'bar-input-var-name': { value: ['value1', 'value2'], type: 'text' }, + 'bar-input2-var-name': { value: 'test', type: 'text' }, + }, + streams: [ + { + id: 'bar-bar', + dataset: 'bar', + enabled: true, + config: { 'var-name': { value: 'test_yaml: value', type: 'yaml' } }, + }, + { + id: 'bar-bar2', + dataset: 'bar2', + enabled: true, + config: { 'var-name': { value: undefined, type: 'text' } }, + }, + ], + }, + { + type: 'with-no-config-or-streams', + enabled: true, + streams: [], + }, + { + type: 'with-disabled-streams', + enabled: true, + streams: [ + { + id: 'with-disabled-streams-disabled', + dataset: 'disabled', + enabled: false, + config: { 'var-name': { value: undefined, type: 'text' } }, + }, + { + id: 'with-disabled-streams-disabled2', + dataset: 'disabled2', + enabled: false, + }, + ], + }, + ], + }; + + const invalidDatasource: NewDatasource = { + ...validDatasource, + name: '', + inputs: [ + { + type: 'foo', + enabled: true, + config: { + 'foo-input-var-name': { value: undefined, type: 'text' }, + 'foo-input2-var-name': { value: '', type: 'text' }, + 'foo-input3-var-name': { value: [], type: 'text' }, + }, + streams: [ + { + id: 'foo-foo', + dataset: 'foo', + enabled: true, + config: { 'var-name': { value: 'invalidyaml: test\n foo bar:', type: 'yaml' } }, + }, + ], + }, + { + type: 'bar', + enabled: true, + config: { + 'bar-input-var-name': { value: 'invalid value for multi', type: 'text' }, + 'bar-input2-var-name': { value: undefined, type: 'text' }, + }, + streams: [ + { + id: 'bar-bar', + dataset: 'bar', + enabled: true, + config: { 'var-name': { value: ' \n\n', type: 'yaml' } }, + }, + { + id: 'bar-bar2', + dataset: 'bar2', + enabled: true, + config: { 'var-name': { value: undefined, type: 'text' } }, + }, + ], + }, + { + type: 'with-no-config-or-streams', + enabled: true, + streams: [], + }, + { + type: 'with-disabled-streams', + enabled: true, + streams: [ + { + id: 'with-disabled-streams-disabled', + dataset: 'disabled', + enabled: false, + config: { + 'var-name': { + value: 'invalid value but not checked due to not enabled', + type: 'text', + }, + }, + }, + { + id: 'with-disabled-streams-disabled2', + dataset: 'disabled2', + enabled: false, + }, + ], + }, + ], + }; + + const noErrorsValidationResults = { + name: null, + description: null, + inputs: { + foo: { + config: { + 'foo-input-var-name': null, + 'foo-input2-var-name': null, + 'foo-input3-var-name': null, + }, + streams: { 'foo-foo': { config: { 'var-name': null } } }, + }, + bar: { + config: { 'bar-input-var-name': null, 'bar-input2-var-name': null }, + streams: { + 'bar-bar': { config: { 'var-name': null } }, + 'bar-bar2': { config: { 'var-name': null } }, + }, + }, + 'with-disabled-streams': { + streams: { 'with-disabled-streams-disabled': { config: { 'var-name': null } } }, + }, + }, + }; + + it('returns no errors for valid datasource configuration', () => { + expect(validateDatasource(validDatasource, mockPackage)).toEqual(noErrorsValidationResults); + }); + + it('returns errors for invalid datasource configuration', () => { + expect(validateDatasource(invalidDatasource, mockPackage)).toEqual({ + name: ['Name is required'], + description: null, + inputs: { + foo: { + config: { + 'foo-input-var-name': null, + 'foo-input2-var-name': ['foo-input2-var-name is required'], + 'foo-input3-var-name': ['foo-input3-var-name is required'], + }, + streams: { 'foo-foo': { config: { 'var-name': ['Invalid YAML format'] } } }, + }, + bar: { + config: { + 'bar-input-var-name': ['Invalid format'], + 'bar-input2-var-name': ['bar-input2-var-name is required'], + }, + streams: { + 'bar-bar': { config: { 'var-name': ['var-name is required'] } }, + 'bar-bar2': { config: { 'var-name': null } }, + }, + }, + 'with-disabled-streams': { + streams: { 'with-disabled-streams-disabled': { config: { 'var-name': null } } }, + }, + }, + }); + }); + + it('returns no errors for disabled inputs', () => { + const disabledInputs = invalidDatasource.inputs.map(input => ({ ...input, enabled: false })); + expect(validateDatasource({ ...validDatasource, inputs: disabledInputs }, mockPackage)).toEqual( + noErrorsValidationResults + ); + }); + + it('returns only datasource and input-level errors for disabled streams', () => { + const inputsWithDisabledStreams = invalidDatasource.inputs.map(input => + input.streams + ? { + ...input, + streams: input.streams.map(stream => ({ ...stream, enabled: false })), + } + : input + ); + expect( + validateDatasource({ ...invalidDatasource, inputs: inputsWithDisabledStreams }, mockPackage) + ).toEqual({ + name: ['Name is required'], + description: null, + inputs: { + foo: { + config: { + 'foo-input-var-name': null, + 'foo-input2-var-name': ['foo-input2-var-name is required'], + 'foo-input3-var-name': ['foo-input3-var-name is required'], + }, + streams: { 'foo-foo': { config: { 'var-name': null } } }, + }, + bar: { + config: { + 'bar-input-var-name': ['Invalid format'], + 'bar-input2-var-name': ['bar-input2-var-name is required'], + }, + streams: { + 'bar-bar': { config: { 'var-name': null } }, + 'bar-bar2': { config: { 'var-name': null } }, + }, + }, + 'with-disabled-streams': { + streams: { 'with-disabled-streams-disabled': { config: { 'var-name': null } } }, + }, + }, + }); + }); + + it('returns no errors for packages with no datasources', () => { + expect( + validateDatasource(validDatasource, { + ...mockPackage, + datasources: undefined, + }) + ).toEqual({ + name: null, + description: null, + inputs: null, + }); + expect( + validateDatasource(validDatasource, { + ...mockPackage, + datasources: [], + }) + ).toEqual({ + name: null, + description: null, + inputs: null, + }); + }); + + it('returns no errors for packages with no inputs', () => { + expect( + validateDatasource(validDatasource, { + ...mockPackage, + datasources: [{} as RegistryDatasource], + }) + ).toEqual({ + name: null, + description: null, + inputs: null, + }); + expect( + validateDatasource(validDatasource, { + ...mockPackage, + datasources: [({ inputs: [] } as unknown) as RegistryDatasource], + }) + ).toEqual({ + name: null, + description: null, + inputs: null, + }); + }); +}); + +describe('Ingest Manager - validationHasErrors()', () => { + it('returns true for stream validation results with errors', () => { + expect( + validationHasErrors({ + config: { foo: ['foo error'], bar: null }, + }) + ).toBe(true); + }); + + it('returns false for stream validation results with no errors', () => { + expect( + validationHasErrors({ + config: { foo: null, bar: null }, + }) + ).toBe(false); + }); + + it('returns true for input validation results with errors', () => { + expect( + validationHasErrors({ + config: { foo: ['foo error'], bar: null }, + streams: { stream1: { config: { foo: null, bar: null } } }, + }) + ).toBe(true); + expect( + validationHasErrors({ + config: { foo: null, bar: null }, + streams: { stream1: { config: { foo: ['foo error'], bar: null } } }, + }) + ).toBe(true); + }); + + it('returns false for input validation results with no errors', () => { + expect( + validationHasErrors({ + config: { foo: null, bar: null }, + streams: { stream1: { config: { foo: null, bar: null } } }, + }) + ).toBe(false); + }); + + it('returns true for datasource validation results with errors', () => { + expect( + validationHasErrors({ + name: ['name error'], + description: null, + inputs: { + input1: { + config: { foo: null, bar: null }, + streams: { stream1: { config: { foo: null, bar: null } } }, + }, + }, + }) + ).toBe(true); + expect( + validationHasErrors({ + name: null, + description: null, + inputs: { + input1: { + config: { foo: ['foo error'], bar: null }, + streams: { stream1: { config: { foo: null, bar: null } } }, + }, + }, + }) + ).toBe(true); + expect( + validationHasErrors({ + name: null, + description: null, + inputs: { + input1: { + config: { foo: null, bar: null }, + streams: { stream1: { config: { foo: ['foo error'], bar: null } } }, + }, + }, + }) + ).toBe(true); + }); + + it('returns false for datasource validation results with no errors', () => { + expect( + validationHasErrors({ + name: null, + description: null, + inputs: { + input1: { + config: { foo: null, bar: null }, + streams: { stream1: { config: { foo: null, bar: null } } }, + }, + }, + }) + ).toBe(false); + }); +}); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/validate_datasource.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/validate_datasource.ts new file mode 100644 index 0000000000000..518e2bfc1af07 --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/services/validate_datasource.ts @@ -0,0 +1,232 @@ +/* + * 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 { safeLoad } from 'js-yaml'; +import { getFlattenedObject } from '../../../../services'; +import { + NewDatasource, + DatasourceInput, + DatasourceInputStream, + DatasourceConfigRecordEntry, + PackageInfo, + RegistryInput, + RegistryVarsEntry, +} from '../../../../types'; + +type Errors = string[] | null; + +type ValidationEntry = Record; + +export interface DatasourceConfigValidationResults { + config?: ValidationEntry; +} + +export type DatasourceInputValidationResults = DatasourceConfigValidationResults & { + streams?: Record; +}; + +export interface DatasourceValidationResults { + name: Errors; + description: Errors; + inputs: Record | null; +} + +/* + * Returns validation information for a given datasource configuration and package info + * Note: this method assumes that `datasource` is correctly structured for the given package + */ +export const validateDatasource = ( + datasource: NewDatasource, + packageInfo: PackageInfo +): DatasourceValidationResults => { + const validationResults: DatasourceValidationResults = { + name: null, + description: null, + inputs: {}, + }; + + if (!datasource.name.trim()) { + validationResults.name = [ + i18n.translate('xpack.ingestManager.datasourceValidation.nameRequiredErrorMessage', { + defaultMessage: 'Name is required', + }), + ]; + } + + if ( + !packageInfo.datasources || + packageInfo.datasources.length === 0 || + !packageInfo.datasources[0] || + !packageInfo.datasources[0].inputs || + packageInfo.datasources[0].inputs.length === 0 + ) { + validationResults.inputs = null; + return validationResults; + } + + const registryInputsByType: Record< + string, + RegistryInput + > = packageInfo.datasources[0].inputs.reduce((inputs, registryInput) => { + inputs[registryInput.type] = registryInput; + return inputs; + }, {} as Record); + + // Validate each datasource input with either its own config fields or streams + datasource.inputs.forEach(input => { + if (!input.config && !input.streams) { + return; + } + + const inputValidationResults: DatasourceInputValidationResults = { + config: undefined, + streams: {}, + }; + + const inputVarsByName = (registryInputsByType[input.type].vars || []).reduce( + (vars, registryVar) => { + vars[registryVar.name] = registryVar; + return vars; + }, + {} as Record + ); + + // Validate input-level config fields + const inputConfigs = Object.entries(input.config || {}); + if (inputConfigs.length) { + inputValidationResults.config = inputConfigs.reduce((results, [name, configEntry]) => { + results[name] = input.enabled + ? validateDatasourceConfig(configEntry, inputVarsByName[name]) + : null; + return results; + }, {} as ValidationEntry); + } else { + delete inputValidationResults.config; + } + + // Validate each input stream with config fields + if (input.streams.length) { + input.streams.forEach(stream => { + if (!stream.config) { + return; + } + + const streamValidationResults: DatasourceConfigValidationResults = { + config: undefined, + }; + + const streamVarsByName = ( + ( + registryInputsByType[input.type].streams.find( + registryStream => registryStream.dataset === stream.dataset + ) || {} + ).vars || [] + ).reduce((vars, registryVar) => { + vars[registryVar.name] = registryVar; + return vars; + }, {} as Record); + + // Validate stream-level config fields + streamValidationResults.config = Object.entries(stream.config).reduce( + (results, [name, configEntry]) => { + results[name] = + input.enabled && stream.enabled + ? validateDatasourceConfig(configEntry, streamVarsByName[name]) + : null; + return results; + }, + {} as ValidationEntry + ); + + inputValidationResults.streams![stream.id] = streamValidationResults; + }); + } else { + delete inputValidationResults.streams; + } + + if (inputValidationResults.config || inputValidationResults.streams) { + validationResults.inputs![input.type] = inputValidationResults; + } + }); + + if (Object.entries(validationResults.inputs!).length === 0) { + validationResults.inputs = null; + } + return validationResults; +}; + +const validateDatasourceConfig = ( + configEntry: DatasourceConfigRecordEntry, + varDef: RegistryVarsEntry +): string[] | null => { + const errors = []; + const { value } = configEntry; + let parsedValue: any = value; + + if (typeof value === 'string') { + parsedValue = value.trim(); + } + + if (varDef.required) { + if (parsedValue === undefined || (typeof parsedValue === 'string' && !parsedValue)) { + errors.push( + i18n.translate('xpack.ingestManager.datasourceValidation.requiredErrorMessage', { + defaultMessage: '{fieldName} is required', + values: { + fieldName: varDef.title || varDef.name, + }, + }) + ); + } + } + + if (varDef.type === 'yaml') { + try { + parsedValue = safeLoad(value); + } catch (e) { + errors.push( + i18n.translate('xpack.ingestManager.datasourceValidation.invalidYamlFormatErrorMessage', { + defaultMessage: 'Invalid YAML format', + }) + ); + } + } + + if (varDef.multi) { + if (parsedValue && !Array.isArray(parsedValue)) { + errors.push( + i18n.translate('xpack.ingestManager.datasourceValidation.invalidArrayErrorMessage', { + defaultMessage: 'Invalid format', + }) + ); + } + if ( + varDef.required && + (!parsedValue || (Array.isArray(parsedValue) && parsedValue.length === 0)) + ) { + errors.push( + i18n.translate('xpack.ingestManager.datasourceValidation.requiredErrorMessage', { + defaultMessage: '{fieldName} is required', + values: { + fieldName: varDef.title || varDef.name, + }, + }) + ); + } + } + + return errors.length ? errors : null; +}; + +export const validationHasErrors = ( + validationResults: + | DatasourceValidationResults + | DatasourceInputValidationResults + | DatasourceConfigValidationResults +) => { + const flattenedValidation = getFlattenedObject(validationResults); + return !!Object.entries(flattenedValidation).find(([, value]) => !!value); +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/step_configure_datasource.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/step_configure_datasource.tsx index b45beef4a8b5e..105d6c66a5704 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/step_configure_datasource.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/step_configure_datasource.tsx @@ -9,17 +9,16 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { EuiSteps, EuiPanel, - EuiFlexGrid, EuiFlexGroup, EuiFlexItem, EuiFormRow, - EuiFieldText, EuiButtonEmpty, EuiSpacer, EuiEmptyPrompt, EuiText, EuiButton, EuiComboBox, + EuiCallOut, } from '@elastic/eui'; import { AgentConfig, @@ -28,21 +27,37 @@ import { NewDatasource, DatasourceInput, } from '../../../types'; +import { Loading } from '../../../components'; import { packageToConfigDatasourceInputs } from '../../../services'; -import { DatasourceInputPanel } from './components'; +import { DatasourceValidationResults, validationHasErrors } from './services'; +import { DatasourceInputPanel, DatasourceInputVarField } from './components'; export const StepConfigureDatasource: React.FunctionComponent<{ agentConfig: AgentConfig; packageInfo: PackageInfo; datasource: NewDatasource; updateDatasource: (fields: Partial) => void; + validationResults: DatasourceValidationResults; backLink: JSX.Element; cancelUrl: string; onNext: () => void; -}> = ({ agentConfig, packageInfo, datasource, updateDatasource, backLink, cancelUrl, onNext }) => { +}> = ({ + agentConfig, + packageInfo, + datasource, + updateDatasource, + validationResults, + backLink, + cancelUrl, + onNext, +}) => { // Form show/hide states const [isShowingAdvancedDefine, setIsShowingAdvancedDefine] = useState(false); + // Form submit state + const [submitAttempted, setSubmitAttempted] = useState(false); + const hasErrors = validationResults ? validationHasErrors(validationResults) : false; + // Update datasource's package and config info useEffect(() => { const dsPackage = datasource.package; @@ -81,56 +96,56 @@ export const StepConfigureDatasource: React.FunctionComponent<{ }, [datasource.package, datasource.config_id, agentConfig, packageInfo, updateDatasource]); // Step A, define datasource - const DefineDatasource = ( + const renderDefineDatasource = () => ( - - - - } - > - - updateDatasource({ - name: e.target.value, - }) - } - /> - + + + { + updateDatasource({ + name: newValue, + }); + }} + errors={validationResults!.name} + forceShowErrors={submitAttempted} + /> - - - } - labelAppend={ - - - - } - > - - updateDatasource({ - description: e.target.value, - }) - } - /> - + + { + updateDatasource({ + description: newValue, + }); + }} + errors={validationResults!.description} + forceShowErrors={submitAttempted} + /> - + - - + + - + + ) : null} @@ -182,7 +198,7 @@ export const StepConfigureDatasource: React.FunctionComponent<{ // Step B, configure inputs (and their streams) // Assume packages only export one datasource for now - const ConfigureInputs = + const renderConfigureInputs = () => packageInfo.datasources && packageInfo.datasources[0] && packageInfo.datasources[0].inputs && @@ -208,6 +224,8 @@ export const StepConfigureDatasource: React.FunctionComponent<{ inputs: newInputs, }); }} + inputValidationResults={validationResults!.inputs![datasourceInput.type]} + forceShowErrors={submitAttempted} /> ) : null; @@ -232,7 +250,7 @@ export const StepConfigureDatasource: React.FunctionComponent<{ ); - return ( + return validationResults ? ( @@ -251,7 +269,7 @@ export const StepConfigureDatasource: React.FunctionComponent<{ defaultMessage: 'Define your datasource', } ), - children: DefineDatasource, + children: renderDefineDatasource(), }, { title: i18n.translate( @@ -260,13 +278,34 @@ export const StepConfigureDatasource: React.FunctionComponent<{ defaultMessage: 'Choose the data you want to collect', } ), - children: ConfigureInputs, + children: renderConfigureInputs(), }, ]} /> + {hasErrors && submitAttempted ? ( + + +

+ +

+
+ +
+ ) : null} @@ -278,7 +317,17 @@ export const StepConfigureDatasource: React.FunctionComponent<{
- onNext()}> + { + setSubmitAttempted(true); + if (!hasErrors) { + onNext(); + } + }} + > + ) : ( + ); }; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/constants.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/constants.tsx index 3a6dfe4a87daf..685199245df18 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/constants.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/constants.tsx @@ -24,6 +24,7 @@ export const AssetTitleMap: Record = { search: 'Saved Search', visualization: 'Visualization', input: 'Agent input', + map: 'Map', }; export const ServiceTitleMap: Record = { @@ -36,6 +37,7 @@ export const AssetIcons: Record = { 'index-pattern': 'indexPatternApp', search: 'searchProfilerApp', visualization: 'visualizeApp', + map: 'mapApp', }; export const ServiceIcons: Record = { diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/services/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/services/index.ts index 0aa08602e4d4d..5ebd1300baf65 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/services/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/services/index.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +export { getFlattenedObject } from '../../../../../../../src/core/utils'; + export { agentConfigRouteService, datasourceRouteService, diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts index 333a9b049fa85..32615278b67d7 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts @@ -16,6 +16,7 @@ export { NewDatasource, DatasourceInput, DatasourceInputStream, + DatasourceConfigRecordEntry, // API schemas - Agent Config GetAgentConfigsResponse, GetAgentConfigsResponseItem, @@ -56,6 +57,7 @@ export { RegistryVarsEntry, RegistryInput, RegistryStream, + RegistryDatasource, PackageList, PackageListItem, PackagesGroupedByStatus, @@ -70,4 +72,5 @@ export { DeletePackageResponse, DetailViewPanelName, InstallStatus, + InstallationStatus, } from '../../../../common'; diff --git a/x-pack/plugins/ingest_manager/server/services/agent_config.ts b/x-pack/plugins/ingest_manager/server/services/agent_config.ts index a941494072ae3..309ddca3784c2 100644 --- a/x-pack/plugins/ingest_manager/server/services/agent_config.ts +++ b/x-pack/plugins/ingest_manager/server/services/agent_config.ts @@ -319,9 +319,9 @@ class AgentConfigService { return outputs; }, {} as FullAgentConfig['outputs']), }, - datasources: (config.datasources as Datasource[]).map(ds => - storedDatasourceToAgentDatasource(ds) - ), + datasources: (config.datasources as Datasource[]) + .filter(datasource => datasource.enabled) + .map(ds => storedDatasourceToAgentDatasource(ds)), revision: config.revision, }; diff --git a/x-pack/plugins/maps/public/components/metric_editor.js b/x-pack/plugins/maps/public/components/metric_editor.js index 530f402592b2b..d1affe2f42190 100644 --- a/x-pack/plugins/maps/public/components/metric_editor.js +++ b/x-pack/plugins/maps/public/components/metric_editor.js @@ -24,8 +24,13 @@ function filterFieldsForAgg(fields, aggType) { return getTermsFields(fields); } + const metricAggFieldTypes = ['number']; + if (aggType !== AGG_TYPE.SUM) { + metricAggFieldTypes.push('date'); + } + return fields.filter(field => { - return field.aggregatable && field.type === 'number'; + return field.aggregatable && metricAggFieldTypes.includes(field.type); }); } diff --git a/x-pack/plugins/maps/public/layers/fields/es_agg_field.ts b/x-pack/plugins/maps/public/layers/fields/es_agg_field.ts index 65f952ca01038..34f7dd4b9578f 100644 --- a/x-pack/plugins/maps/public/layers/fields/es_agg_field.ts +++ b/x-pack/plugins/maps/public/layers/fields/es_agg_field.ts @@ -90,7 +90,7 @@ export class ESAggField implements IESAggField { async createTooltipProperty(value: string | undefined): Promise { const indexPattern = await this._source.getIndexPattern(); const tooltipProperty = new TooltipProperty(this.getName(), await this.getLabel(), value); - return new ESAggTooltipProperty(tooltipProperty, indexPattern, this); + return new ESAggTooltipProperty(tooltipProperty, indexPattern, this, this.getAggType()); } getValueAggDsl(indexPattern: IndexPattern): unknown | null { diff --git a/x-pack/plugins/maps/public/layers/tooltips/es_agg_tooltip_property.ts b/x-pack/plugins/maps/public/layers/tooltips/es_agg_tooltip_property.ts index 24011c51ddbaa..acd05475f9762 100644 --- a/x-pack/plugins/maps/public/layers/tooltips/es_agg_tooltip_property.ts +++ b/x-pack/plugins/maps/public/layers/tooltips/es_agg_tooltip_property.ts @@ -4,9 +4,25 @@ * you may not use this file except in compliance with the Elastic License. */ import { ESTooltipProperty } from './es_tooltip_property'; +import { AGG_TYPE } from '../../../common/constants'; +import { ITooltipProperty } from './tooltip_property'; +import { IField } from '../fields/field'; +import { IndexPattern } from '../../../../../../src/plugins/data/public'; export class ESAggTooltipProperty extends ESTooltipProperty { + private readonly _aggType: AGG_TYPE; + + constructor( + tooltipProperty: ITooltipProperty, + indexPattern: IndexPattern, + field: IField, + aggType: AGG_TYPE + ) { + super(tooltipProperty, indexPattern, field); + this._aggType = aggType; + } + isFilterable(): boolean { - return false; + return this._aggType === AGG_TYPE.TERMS; } } diff --git a/x-pack/plugins/ml/common/constants/file_datavisualizer.ts b/x-pack/plugins/ml/common/constants/file_datavisualizer.ts index d72e4d63cc47e..81d51bfa25816 100644 --- a/x-pack/plugins/ml/common/constants/file_datavisualizer.ts +++ b/x-pack/plugins/ml/common/constants/file_datavisualizer.ts @@ -5,6 +5,8 @@ */ export const MAX_BYTES = 104857600; +export const ABSOLUTE_MAX_BYTES = MAX_BYTES * 5; +export const FILE_SIZE_DISPLAY_FORMAT = '0,0.[0] b'; // Value to use in the Elasticsearch index mapping meta data to identify the // index as having been created by the ML File Data Visualizer. diff --git a/x-pack/plugins/ml/common/types/ml_config.ts b/x-pack/plugins/ml/common/types/ml_config.ts new file mode 100644 index 0000000000000..8fd9fd22bad8a --- /dev/null +++ b/x-pack/plugins/ml/common/types/ml_config.ts @@ -0,0 +1,16 @@ +/* + * 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 { schema, TypeOf } from '@kbn/config-schema'; +import { MAX_BYTES } from '../constants/file_datavisualizer'; + +export const configSchema = schema.object({ + file_data_visualizer: schema.object({ + max_file_size_bytes: schema.number({ defaultValue: MAX_BYTES }), + }), +}); + +export type MlConfigType = TypeOf; diff --git a/x-pack/plugins/ml/public/application/app.tsx b/x-pack/plugins/ml/public/application/app.tsx index e9796fcbb0fe4..f1facd18b9da5 100644 --- a/x-pack/plugins/ml/public/application/app.tsx +++ b/x-pack/plugins/ml/public/application/app.tsx @@ -15,10 +15,14 @@ import { KibanaContextProvider } from '../../../../../src/plugins/kibana_react/p import { setDependencyCache, clearCache } from './util/dependency_cache'; import { setLicenseCache } from './license'; import { MlSetupDependencies, MlStartDependencies } from '../plugin'; +import { MlConfigType } from '../../common/types/ml_config'; import { MlRouter } from './routing'; -type MlDependencies = MlSetupDependencies & MlStartDependencies; +type MlDependencies = MlSetupDependencies & + MlStartDependencies & { + mlConfig: MlConfigType; + }; interface AppProps { coreStart: CoreStart; @@ -74,6 +78,7 @@ export const renderApp = ( http: coreStart.http, security: deps.security, urlGenerators: deps.share.urlGenerators, + mlConfig: deps.mlConfig, }); const mlLicense = setLicenseCache(deps.licensing); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_form/create_analytics_form.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_form/create_analytics_form.tsx index e5f30a50ed8f0..0c83dfb6a2346 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_form/create_analytics_form.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_form/create_analytics_form.tsx @@ -250,7 +250,7 @@ export const CreateAnalyticsForm: FC = ({ actions, sta dependentVariableOptions: [] as State['form']['dependentVariableOptions'], }; - await newJobCapsService.initializeFromIndexPattern(indexPattern); + await newJobCapsService.initializeFromIndexPattern(indexPattern, false, false); // Get fields and filter for supported types for job type const { fields } = newJobCapsService; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_form/form_options_validation.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_form/form_options_validation.ts index 9c0bc69f4b41f..4bd03fec7cc72 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_form/form_options_validation.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/create_analytics_form/form_options_validation.ts @@ -10,7 +10,7 @@ import { ANALYSIS_CONFIG_TYPE } from '../../../../common/analytics'; import { AnalyticsJobType } from '../../hooks/use_create_analytics_form/state'; import { BASIC_NUMERICAL_TYPES, EXTENDED_NUMERICAL_TYPES } from '../../../../common/fields'; -const CATEGORICAL_TYPES = new Set(['ip', 'keyword', 'text']); +const CATEGORICAL_TYPES = new Set(['ip', 'keyword']); // List of system fields we want to ignore for the numeric field check. export const OMIT_FIELDS: string[] = ['_source', '_type', '_index', '_id', '_version', '_score']; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts index 01a39d2ef9f3b..e121268e65e86 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts @@ -8,6 +8,7 @@ import { EuiComboBoxOptionOption } from '@elastic/eui'; import { DeepPartial, DeepReadonly } from '../../../../../../../common/types/common'; import { checkPermission } from '../../../../../privilege/check_privilege'; import { mlNodesAvailable } from '../../../../../ml_nodes_check'; +import { newJobCapsService } from '../../../../../services/new_job_capabilities_service'; import { isClassificationAnalysis, @@ -158,6 +159,55 @@ export const getInitialState = (): State => ({ estimatedModelMemoryLimit: '', }); +const getExcludesFields = (excluded: string[]) => { + const { fields } = newJobCapsService; + const updatedExcluded: string[] = []; + // Loop through excluded fields to check for multiple types of same field + for (let i = 0; i < excluded.length; i++) { + const fieldName = excluded[i]; + let mainField; + + // No dot in fieldName - it is the main field + if (fieldName.includes('.') === false) { + mainField = fieldName; + } else { + // Dot in fieldName - check if there's a field whose name equals the fieldName with the last dot suffix removed + const regex = /\.[^.]*$/; + const suffixRemovedField = fieldName.replace(regex, ''); + const fieldMatch = newJobCapsService.getFieldById(suffixRemovedField); + + // There's a match - set as the main field + if (fieldMatch !== null) { + mainField = suffixRemovedField; + } else { + // No main field to be found - add the fieldName to updatedExcluded array if it's not already there + if (updatedExcluded.includes(fieldName) === false) { + updatedExcluded.push(fieldName); + } + } + } + + if (mainField !== undefined) { + // Add the main field to the updatedExcluded array if it's not already there + if (updatedExcluded.includes(mainField) === false) { + updatedExcluded.push(mainField); + } + // Create regex to find all other fields whose names begin with main field followed by a dot + const regex = new RegExp(`${mainField}\\..+`); + + // Loop through fields and add fields matching the pattern to updatedExcluded array + for (let j = 0; j < fields.length; j++) { + const field = fields[j].name; + if (updatedExcluded.includes(field) === false && field.match(regex) !== null) { + updatedExcluded.push(field); + } + } + } + } + + return updatedExcluded; +}; + export const getJobConfigFromFormState = ( formState: State['form'] ): DeepPartial => { @@ -175,7 +225,7 @@ export const getJobConfigFromFormState = ( index: formState.destinationIndex, }, analyzed_fields: { - excludes: formState.excludes, + excludes: getExcludesFields(formState.excludes), }, analysis: { outlier_detection: {}, diff --git a/x-pack/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx b/x-pack/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx index a8bb5a0a8fe10..2d6505f5ce1f7 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/datavisualizer_selector.tsx @@ -26,6 +26,7 @@ import { isFullLicense } from '../license'; import { useTimefilter, useMlKibana } from '../contexts/kibana'; import { NavigationMenu } from '../components/navigation_menu'; +import { getMaxBytesFormatted } from './file_based/components/utils'; function startTrialDescription() { return ( @@ -59,6 +60,8 @@ export const DatavisualizerSelector: FC = () => { licenseManagement.enabled === true && isFullLicense() === false; + const maxFileSize = getMaxBytesFormatted(); + return ( @@ -102,7 +105,8 @@ export const DatavisualizerSelector: FC = () => { description={ } betaBadgeLabel={i18n.translate( diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/welcome_content.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/welcome_content.tsx index c56ab021e2f2f..49b2396aeca13 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/welcome_content.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/welcome_content.tsx @@ -19,6 +19,7 @@ import { } from '@elastic/eui'; import { ExperimentalBadge } from '../experimental_badge'; +import { getMaxBytesFormatted } from '../utils'; export const WelcomeContent: FC = () => { const toolTipContent = i18n.translate( @@ -28,6 +29,8 @@ export const WelcomeContent: FC = () => { } ); + const maxFileSize = getMaxBytesFormatted(); + return ( @@ -117,7 +120,8 @@ export const WelcomeContent: FC = () => {

diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js index 02f14a9e4553e..d1b615a878b2b 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js @@ -19,14 +19,15 @@ import { FileCouldNotBeRead, FileTooLarge } from './file_error_callouts'; import { EditFlyout } from '../edit_flyout'; import { ExplanationFlyout } from '../explanation_flyout'; import { ImportView } from '../import_view'; -import { MAX_BYTES } from '../../../../../../common/constants/file_datavisualizer'; import { + getMaxBytes, readFile, createUrlOverrides, processResults, reduceData, hasImportPermission, } from '../utils'; + import { MODE } from './constants'; const UPLOAD_SIZE_MB = 5; @@ -57,6 +58,7 @@ export class FileDataVisualizerView extends Component { this.overrides = {}; this.previousOverrides = {}; this.originalSettings = {}; + this.maxFileUploadBytes = getMaxBytes(); } async componentDidMount() { @@ -93,7 +95,7 @@ export class FileDataVisualizerView extends Component { }; async loadFile(file) { - if (file.size <= MAX_BYTES) { + if (file.size <= this.maxFileUploadBytes) { try { const fileContents = await readFile(file); const data = fileContents.data; @@ -105,7 +107,6 @@ export class FileDataVisualizerView extends Component { await this.loadSettings(data); } catch (error) { - console.error(error); this.setState({ loaded: false, loading: false, @@ -181,8 +182,6 @@ export class FileDataVisualizerView extends Component { fileCouldNotBeRead: isRetry, }); } catch (error) { - console.error(error); - this.setState({ results: undefined, explanation: undefined, @@ -287,7 +286,9 @@ export class FileDataVisualizerView extends Component { {loading && } - {fileTooLarge && } + {fileTooLarge && ( + + )} {fileCouldNotBeRead && loading === false && ( diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_error_callouts.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_error_callouts.tsx index 3dbc20f16012b..7333c96a0d8ea 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_error_callouts.tsx +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_datavisualizer_view/file_error_callouts.tsx @@ -11,8 +11,7 @@ import { EuiCallOut, EuiSpacer } from '@elastic/eui'; import numeral from '@elastic/numeral'; import { ErrorResponse } from '../../../../../../common/types/errors'; - -const FILE_SIZE_DISPLAY_FORMAT = '0,0.[0] b'; +import { FILE_SIZE_DISPLAY_FORMAT } from '../../../../../../common/constants/file_datavisualizer'; interface FileTooLargeProps { fileSize: number; @@ -81,7 +80,7 @@ interface FileCouldNotBeReadProps { } export const FileCouldNotBeRead: FC = ({ error, loaded }) => { - const message = error.body.message; + const message = error?.body?.message || ''; return ( = ({ error, loaded }; export const Explanation: FC<{ error: ErrorResponse }> = ({ error }) => { - if (!error.body.attributes?.body?.error?.suppressed?.length) { + if (!error?.body?.attributes?.body?.error?.suppressed?.length) { return null; } const reason: string = error.body.attributes.body.error.suppressed[0].reason; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/index.ts index 6f670359586b7..0f0036a7c4616 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/index.ts +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/index.ts @@ -10,4 +10,6 @@ export { processResults, readFile, reduceData, + getMaxBytes, + getMaxBytesFormatted, } from './utils'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/utils.ts b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/utils.ts index 5048065ae60fa..0d2016b71ed83 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/utils.ts +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/utils.ts @@ -5,8 +5,14 @@ */ import { isEqual } from 'lodash'; +import numeral from '@elastic/numeral'; import { ml } from '../../../../services/ml_api_service'; import { AnalysisResult, InputOverrides } from '../../../../../../common/types/file_datavisualizer'; +import { + ABSOLUTE_MAX_BYTES, + FILE_SIZE_DISPLAY_FORMAT, +} from '../../../../../../common/constants/file_datavisualizer'; +import { getMlConfig } from '../../../../util/dependency_cache'; const DEFAULT_LINES_TO_SAMPLE = 1000; @@ -54,6 +60,15 @@ export function reduceData(data: string, mb: number) { return data.length >= size ? data.slice(0, size) : data; } +export function getMaxBytes() { + const maxBytes = getMlConfig().file_data_visualizer.max_file_size_bytes; + return maxBytes < ABSOLUTE_MAX_BYTES ? maxBytes : ABSOLUTE_MAX_BYTES; +} + +export function getMaxBytesFormatted() { + return numeral(getMaxBytes()).format(FILE_SIZE_DISPLAY_FORMAT); +} + export function createUrlOverrides(overrides: InputOverrides, originalSettings: InputOverrides) { const formattedOverrides: InputOverrides = {}; for (const o in overrideDefaults) { diff --git a/x-pack/plugins/ml/public/application/util/dependency_cache.ts b/x-pack/plugins/ml/public/application/util/dependency_cache.ts index d5605d3bca65f..934a0a5e9ae3a 100644 --- a/x-pack/plugins/ml/public/application/util/dependency_cache.ts +++ b/x-pack/plugins/ml/public/application/util/dependency_cache.ts @@ -23,6 +23,7 @@ import { } from 'kibana/public'; import { SharePluginStart } from 'src/plugins/share/public'; import { SecurityPluginSetup } from '../../../../security/public'; +import { MlConfigType } from '../../../common/types/ml_config'; export interface DependencyCache { timefilter: DataPublicPluginSetup['query']['timefilter'] | null; @@ -42,6 +43,7 @@ export interface DependencyCache { security: SecurityPluginSetup | null; i18n: I18nStart | null; urlGenerators: SharePluginStart['urlGenerators'] | null; + mlConfig: MlConfigType | null; } const cache: DependencyCache = { @@ -62,6 +64,7 @@ const cache: DependencyCache = { security: null, i18n: null, urlGenerators: null, + mlConfig: null, }; export function setDependencyCache(deps: Partial) { @@ -82,6 +85,7 @@ export function setDependencyCache(deps: Partial) { cache.security = deps.security || null; cache.i18n = deps.i18n || null; cache.urlGenerators = deps.urlGenerators || null; + cache.mlConfig = deps.mlConfig || null; } export function getTimefilter() { @@ -202,6 +206,13 @@ export function getGetUrlGenerator() { return cache.urlGenerators.getUrlGenerator; } +export function getMlConfig() { + if (cache.mlConfig === null) { + throw new Error("mlConfig hasn't been initialized"); + } + return cache.mlConfig; +} + export function clearCache() { console.log('clearing dependency cache'); // eslint-disable-line no-console Object.keys(cache).forEach(k => { diff --git a/x-pack/plugins/ml/public/index.ts b/x-pack/plugins/ml/public/index.ts index 8070f94a1264d..4697496270edf 100755 --- a/x-pack/plugins/ml/public/index.ts +++ b/x-pack/plugins/ml/public/index.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { PluginInitializer } from 'kibana/public'; +import { PluginInitializer, PluginInitializerContext } from 'kibana/public'; import './index.scss'; import { MlPlugin, @@ -19,6 +19,6 @@ export const plugin: PluginInitializer< MlPluginStart, MlSetupDependencies, MlStartDependencies -> = () => new MlPlugin(); +> = (context: PluginInitializerContext) => new MlPlugin(context); export { MlPluginSetup, MlPluginStart }; diff --git a/x-pack/plugins/ml/public/plugin.ts b/x-pack/plugins/ml/public/plugin.ts index 2472c343d0510..b51be4d248683 100644 --- a/x-pack/plugins/ml/public/plugin.ts +++ b/x-pack/plugins/ml/public/plugin.ts @@ -5,7 +5,13 @@ */ import { i18n } from '@kbn/i18n'; -import { Plugin, CoreStart, CoreSetup, AppMountParameters } from 'kibana/public'; +import { + Plugin, + CoreStart, + CoreSetup, + AppMountParameters, + PluginInitializerContext, +} from 'kibana/public'; import { ManagementSetup } from 'src/plugins/management/public'; import { SharePluginStart } from 'src/plugins/share/public'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; @@ -19,6 +25,7 @@ import { LicenseManagementUIPluginSetup } from '../../license_management/public' import { setDependencyCache } from './application/util/dependency_cache'; import { PLUGIN_ID, PLUGIN_ICON } from '../common/constants/app'; import { registerFeature } from './register_feature'; +import { MlConfigType } from '../common/types/ml_config'; export interface MlStartDependencies { data: DataPublicPluginStart; @@ -34,7 +41,10 @@ export interface MlSetupDependencies { } export class MlPlugin implements Plugin { + constructor(private readonly initializerContext: PluginInitializerContext) {} + setup(core: CoreSetup, pluginsSetup: MlSetupDependencies) { + const mlConfig = this.initializerContext.config.get(); core.application.register({ id: PLUGIN_ID, title: i18n.translate('xpack.ml.plugin.title', { @@ -57,6 +67,7 @@ export class MlPlugin implements Plugin { usageCollection: pluginsSetup.usageCollection, licenseManagement: pluginsSetup.licenseManagement, home: pluginsSetup.home, + mlConfig, }, { element: params.element, diff --git a/x-pack/plugins/ml/server/config.ts b/x-pack/plugins/ml/server/config.ts new file mode 100644 index 0000000000000..7cef6f17bbefb --- /dev/null +++ b/x-pack/plugins/ml/server/config.ts @@ -0,0 +1,15 @@ +/* + * 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 { PluginConfigDescriptor } from 'kibana/server'; +import { MlConfigType, configSchema } from '../common/types/ml_config'; + +export const config: PluginConfigDescriptor = { + exposeToBrowser: { + file_data_visualizer: true, + }, + schema: configSchema, +}; diff --git a/x-pack/plugins/ml/server/index.ts b/x-pack/plugins/ml/server/index.ts index 175c20bf49c94..6e638d647a387 100644 --- a/x-pack/plugins/ml/server/index.ts +++ b/x-pack/plugins/ml/server/index.ts @@ -9,3 +9,5 @@ import { MlServerPlugin } from './plugin'; export { MlPluginSetup, MlPluginStart } from './plugin'; export const plugin = (ctx: PluginInitializerContext) => new MlServerPlugin(ctx); + +export { config } from './config'; diff --git a/x-pack/plugins/ml/server/plugin.ts b/x-pack/plugins/ml/server/plugin.ts index 7d3ef116e67ab..c7add12be142c 100644 --- a/x-pack/plugins/ml/server/plugin.ts +++ b/x-pack/plugins/ml/server/plugin.ts @@ -79,19 +79,36 @@ export class MlServerPlugin implements Plugin ({ + executeActions: jest.fn(), + getUiMessage: jest.fn(), +})); + +jest.mock('../lib/alerts/get_prepared_alert', () => ({ + getPreparedAlert: jest.fn(() => { + return { + emailAddress: 'foo@foo.com', + }; + }), +})); + +interface MockServices { + callCluster: jest.Mock; + alertInstanceFactory: jest.Mock; + savedObjectsClient: jest.Mock; +} + +describe('getClusterState', () => { + const services: MockServices | AlertServices = { + callCluster: jest.fn(), + alertInstanceFactory: jest.fn(), + savedObjectsClient: savedObjectsClientMock.create(), + }; + + const params: AlertCommonParams = { + dateFormat: 'YYYY', + timezone: 'UTC', + }; + + const emailAddress = 'foo@foo.com'; + const clusterUuid = 'kdksdfj434'; + const clusterName = 'monitoring_test'; + const cluster = { clusterUuid, clusterName }; + + async function setupAlert( + previousState: AlertClusterStateState, + newState: AlertClusterStateState + ): Promise { + const logger: Logger = { + warn: jest.fn(), + log: jest.fn(), + debug: jest.fn(), + trace: jest.fn(), + error: jest.fn(), + fatal: jest.fn(), + info: jest.fn(), + get: jest.fn(), + }; + const getLogger = (): Logger => logger; + const ccrEnabled = false; + (getPreparedAlert as jest.Mock).mockImplementation(() => ({ + emailAddress, + data: [ + { + state: newState, + clusterUuid, + }, + ], + clusters: [cluster], + })); + + const alert = getClusterState(null as any, null as any, getLogger, ccrEnabled); + const state: AlertCommonState = { + [clusterUuid]: { + state: previousState, + ui: { + isFiring: false, + severity: 0, + message: null, + resolvedMS: 0, + lastCheckedMS: 0, + triggeredMS: 0, + }, + } as AlertClusterStatePerClusterState, + }; + + return (await alert.executor({ services, params, state } as any)) as AlertCommonState; + } + + afterEach(() => { + (executeActions as jest.Mock).mockClear(); + }); + + it('should configure the alert properly', () => { + const alert = getClusterState(null as any, null as any, jest.fn(), false); + expect(alert.id).toBe(ALERT_TYPE_CLUSTER_STATE); + expect(alert.actionGroups).toEqual([{ id: 'default', name: 'Default' }]); + }); + + it('should alert if green -> yellow', async () => { + const result = await setupAlert(AlertClusterStateState.Green, AlertClusterStateState.Yellow); + expect(executeActions).toHaveBeenCalledWith( + undefined, + cluster, + AlertClusterStateState.Yellow, + emailAddress + ); + const clusterResult = result[clusterUuid] as AlertClusterStatePerClusterState; + expect(clusterResult.state).toBe(AlertClusterStateState.Yellow); + expect(clusterResult.ui.isFiring).toBe(true); + expect(clusterResult.ui.resolvedMS).toBe(0); + }); + + it('should alert if yellow -> green', async () => { + const result = await setupAlert(AlertClusterStateState.Yellow, AlertClusterStateState.Green); + expect(executeActions).toHaveBeenCalledWith( + undefined, + cluster, + AlertClusterStateState.Green, + emailAddress, + true + ); + const clusterResult = result[clusterUuid] as AlertClusterStatePerClusterState; + expect(clusterResult.state).toBe(AlertClusterStateState.Green); + expect(clusterResult.ui.resolvedMS).toBeGreaterThan(0); + }); + + it('should alert if green -> red', async () => { + const result = await setupAlert(AlertClusterStateState.Green, AlertClusterStateState.Red); + expect(executeActions).toHaveBeenCalledWith( + undefined, + cluster, + AlertClusterStateState.Red, + emailAddress + ); + const clusterResult = result[clusterUuid] as AlertClusterStatePerClusterState; + expect(clusterResult.state).toBe(AlertClusterStateState.Red); + expect(clusterResult.ui.isFiring).toBe(true); + expect(clusterResult.ui.resolvedMS).toBe(0); + }); + + it('should alert if red -> green', async () => { + const result = await setupAlert(AlertClusterStateState.Red, AlertClusterStateState.Green); + expect(executeActions).toHaveBeenCalledWith( + undefined, + cluster, + AlertClusterStateState.Green, + emailAddress, + true + ); + const clusterResult = result[clusterUuid] as AlertClusterStatePerClusterState; + expect(clusterResult.state).toBe(AlertClusterStateState.Green); + expect(clusterResult.ui.resolvedMS).toBeGreaterThan(0); + }); + + it('should not alert if red -> yellow', async () => { + const result = await setupAlert(AlertClusterStateState.Red, AlertClusterStateState.Yellow); + expect(executeActions).not.toHaveBeenCalled(); + const clusterResult = result[clusterUuid] as AlertClusterStatePerClusterState; + expect(clusterResult.state).toBe(AlertClusterStateState.Red); + expect(clusterResult.ui.resolvedMS).toBe(0); + }); + + it('should not alert if yellow -> red', async () => { + const result = await setupAlert(AlertClusterStateState.Yellow, AlertClusterStateState.Red); + expect(executeActions).not.toHaveBeenCalled(); + const clusterResult = result[clusterUuid] as AlertClusterStatePerClusterState; + expect(clusterResult.state).toBe(AlertClusterStateState.Yellow); + expect(clusterResult.ui.resolvedMS).toBe(0); + }); + + it('should not alert if green -> green', async () => { + const result = await setupAlert(AlertClusterStateState.Green, AlertClusterStateState.Green); + expect(executeActions).not.toHaveBeenCalled(); + const clusterResult = result[clusterUuid] as AlertClusterStatePerClusterState; + expect(clusterResult.state).toBe(AlertClusterStateState.Green); + expect(clusterResult.ui.resolvedMS).toBe(0); + }); +}); diff --git a/x-pack/plugins/monitoring/server/alerts/cluster_state.ts b/x-pack/plugins/monitoring/server/alerts/cluster_state.ts new file mode 100644 index 0000000000000..9a5805b8af7ce --- /dev/null +++ b/x-pack/plugins/monitoring/server/alerts/cluster_state.ts @@ -0,0 +1,134 @@ +/* + * 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 moment from 'moment-timezone'; +import { i18n } from '@kbn/i18n'; +import { Logger, ICustomClusterClient, UiSettingsServiceStart } from 'src/core/server'; +import { ALERT_TYPE_CLUSTER_STATE } from '../../common/constants'; +import { AlertType } from '../../../alerting/server'; +import { executeActions, getUiMessage } from '../lib/alerts/cluster_state.lib'; +import { + AlertCommonExecutorOptions, + AlertCommonState, + AlertClusterStatePerClusterState, + AlertCommonCluster, +} from './types'; +import { AlertClusterStateState } from './enums'; +import { getPreparedAlert } from '../lib/alerts/get_prepared_alert'; +import { fetchClusterState } from '../lib/alerts/fetch_cluster_state'; + +export const getClusterState = ( + getUiSettingsService: () => Promise, + monitoringCluster: ICustomClusterClient, + getLogger: (...scopes: string[]) => Logger, + ccsEnabled: boolean +): AlertType => { + const logger = getLogger(ALERT_TYPE_CLUSTER_STATE); + return { + id: ALERT_TYPE_CLUSTER_STATE, + name: 'Monitoring Alert - Cluster Status', + actionGroups: [ + { + id: 'default', + name: i18n.translate('xpack.monitoring.alerts.clusterState.actionGroups.default', { + defaultMessage: 'Default', + }), + }, + ], + defaultActionGroupId: 'default', + async executor({ + services, + params, + state, + }: AlertCommonExecutorOptions): Promise { + logger.debug( + `Firing alert with params: ${JSON.stringify(params)} and state: ${JSON.stringify(state)}` + ); + + const preparedAlert = await getPreparedAlert( + ALERT_TYPE_CLUSTER_STATE, + getUiSettingsService, + monitoringCluster, + logger, + ccsEnabled, + services, + fetchClusterState + ); + + if (!preparedAlert) { + return state; + } + + const { emailAddress, data: states, clusters } = preparedAlert; + + const result: AlertCommonState = { ...state }; + const defaultAlertState: AlertClusterStatePerClusterState = { + state: AlertClusterStateState.Green, + ui: { + isFiring: false, + message: null, + severity: 0, + resolvedMS: 0, + triggeredMS: 0, + lastCheckedMS: 0, + }, + }; + + for (const clusterState of states) { + const alertState: AlertClusterStatePerClusterState = + (state[clusterState.clusterUuid] as AlertClusterStatePerClusterState) || + defaultAlertState; + const cluster = clusters.find( + (c: AlertCommonCluster) => c.clusterUuid === clusterState.clusterUuid + ); + if (!cluster) { + logger.warn(`Unable to find cluster for clusterUuid='${clusterState.clusterUuid}'`); + continue; + } + const isNonGreen = clusterState.state !== AlertClusterStateState.Green; + const severity = clusterState.state === AlertClusterStateState.Red ? 2100 : 1100; + + const ui = alertState.ui; + let triggered = ui.triggeredMS; + let resolved = ui.resolvedMS; + let message = ui.message || {}; + let lastState = alertState.state; + const instance = services.alertInstanceFactory(ALERT_TYPE_CLUSTER_STATE); + + if (isNonGreen) { + if (lastState === AlertClusterStateState.Green) { + logger.debug(`Cluster state changed from green to ${clusterState.state}`); + executeActions(instance, cluster, clusterState.state, emailAddress); + lastState = clusterState.state; + triggered = moment().valueOf(); + } + message = getUiMessage(clusterState.state); + resolved = 0; + } else if (!isNonGreen && lastState !== AlertClusterStateState.Green) { + logger.debug(`Cluster state changed from ${lastState} to green`); + executeActions(instance, cluster, clusterState.state, emailAddress, true); + lastState = clusterState.state; + message = getUiMessage(clusterState.state, true); + resolved = moment().valueOf(); + } + + result[clusterState.clusterUuid] = { + state: lastState, + ui: { + message, + isFiring: isNonGreen, + severity, + resolvedMS: resolved, + triggeredMS: triggered, + lastCheckedMS: moment().valueOf(), + }, + } as AlertClusterStatePerClusterState; + } + + return result; + }, + }; +}; diff --git a/x-pack/plugins/monitoring/server/alerts/enums.ts b/x-pack/plugins/monitoring/server/alerts/enums.ts new file mode 100644 index 0000000000000..ccff588743af1 --- /dev/null +++ b/x-pack/plugins/monitoring/server/alerts/enums.ts @@ -0,0 +1,16 @@ +/* + * 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 enum AlertClusterStateState { + Green = 'green', + Red = 'red', + Yellow = 'yellow', +} + +export enum AlertCommonPerClusterMessageTokenType { + Time = 'time', + Link = 'link', +} diff --git a/x-pack/plugins/monitoring/server/alerts/license_expiration.test.ts b/x-pack/plugins/monitoring/server/alerts/license_expiration.test.ts index 0773af6e7f070..92047e300bc1f 100644 --- a/x-pack/plugins/monitoring/server/alerts/license_expiration.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/license_expiration.test.ts @@ -6,42 +6,31 @@ import moment from 'moment-timezone'; import { getLicenseExpiration } from './license_expiration'; -import { - ALERT_TYPE_LICENSE_EXPIRATION, - MONITORING_CONFIG_ALERTING_EMAIL_ADDRESS, -} from '../../common/constants'; +import { ALERT_TYPE_LICENSE_EXPIRATION } from '../../common/constants'; import { Logger } from 'src/core/server'; -import { AlertServices, AlertInstance } from '../../../alerting/server'; +import { AlertServices } from '../../../alerting/server'; import { savedObjectsClientMock } from 'src/core/server/mocks'; import { - AlertState, - AlertClusterState, - AlertParams, - LicenseExpirationAlertExecutorOptions, + AlertCommonParams, + AlertCommonState, + AlertLicensePerClusterState, + AlertLicense, } from './types'; -import { SavedObject, SavedObjectAttributes } from 'src/core/server'; -import { SavedObjectsClientContract } from 'src/core/server'; - -function fillLicense(license: any, clusterUuid?: string) { - return { - hits: { - hits: [ - { - _source: { - license, - cluster_uuid: clusterUuid, - }, - }, - ], - }, - }; -} - -const clusterUuid = 'a4545jhjb'; -const params: AlertParams = { - dateFormat: 'YYYY', - timezone: 'UTC', -}; +import { executeActions } from '../lib/alerts/license_expiration.lib'; +import { PreparedAlert, getPreparedAlert } from '../lib/alerts/get_prepared_alert'; + +jest.mock('../lib/alerts/license_expiration.lib', () => ({ + executeActions: jest.fn(), + getUiMessage: jest.fn(), +})); + +jest.mock('../lib/alerts/get_prepared_alert', () => ({ + getPreparedAlert: jest.fn(() => { + return { + emailAddress: 'foo@foo.com', + }; + }), +})); interface MockServices { callCluster: jest.Mock; @@ -49,428 +38,169 @@ interface MockServices { savedObjectsClient: jest.Mock; } -const alertExecutorOptions: LicenseExpirationAlertExecutorOptions = { - alertId: '', - startedAt: new Date(), - services: { - callCluster: (path: string, opts: any) => new Promise(resolve => resolve()), - alertInstanceFactory: (id: string) => new AlertInstance(), - savedObjectsClient: {} as jest.Mocked, - }, - params: {}, - state: {}, - spaceId: '', - name: '', - tags: [], - previousStartedAt: null, - createdBy: null, - updatedBy: null, -}; - describe('getLicenseExpiration', () => { - const emailAddress = 'foo@foo.com'; - const getUiSettingsService: any = () => ({ - asScopedToClient: (): any => ({ - get: () => new Promise(resolve => resolve(emailAddress)), - }), - }); - const monitoringCluster: any = null; - const logger: Logger = { - warn: jest.fn(), - log: jest.fn(), - debug: jest.fn(), - trace: jest.fn(), - error: jest.fn(), - fatal: jest.fn(), - info: jest.fn(), - get: jest.fn(), + const services: MockServices | AlertServices = { + callCluster: jest.fn(), + alertInstanceFactory: jest.fn(), + savedObjectsClient: savedObjectsClientMock.create(), }; - const getLogger = (): Logger => logger; - const ccrEnabled = false; - afterEach(() => { - (logger.warn as jest.Mock).mockClear(); - }); - - it('should have the right id and actionGroups', () => { - const alert = getLicenseExpiration( - getUiSettingsService, - monitoringCluster, - getLogger, - ccrEnabled - ); - expect(alert.id).toBe(ALERT_TYPE_LICENSE_EXPIRATION); - expect(alert.actionGroups).toEqual([{ id: 'default', name: 'Default' }]); - }); + const params: AlertCommonParams = { + dateFormat: 'YYYY', + timezone: 'UTC', + }; - it('should return the state if no license is provided', async () => { - const alert = getLicenseExpiration( - getUiSettingsService, - monitoringCluster, - getLogger, - ccrEnabled - ); + const emailAddress = 'foo@foo.com'; + const clusterUuid = 'kdksdfj434'; + const clusterName = 'monitoring_test'; + const dateFormat = 'YYYY-MM-DD'; + const cluster = { clusterUuid, clusterName }; + const defaultUiState = { + isFiring: false, + severity: 0, + message: null, + resolvedMS: 0, + lastCheckedMS: 0, + triggeredMS: 0, + }; - const services: MockServices | AlertServices = { - callCluster: jest.fn(), - alertInstanceFactory: jest.fn(), - savedObjectsClient: savedObjectsClientMock.create(), + async function setupAlert( + license: AlertLicense | null, + expiredCheckDateMS: number, + preparedAlertResponse: PreparedAlert | null | undefined = undefined + ): Promise { + const logger: Logger = { + warn: jest.fn(), + log: jest.fn(), + debug: jest.fn(), + trace: jest.fn(), + error: jest.fn(), + fatal: jest.fn(), + info: jest.fn(), + get: jest.fn(), }; - const state = { foo: 1 }; - - const result = await alert.executor({ - ...alertExecutorOptions, - services, - params, - state, - }); - - expect(result).toEqual(state); - }); + const getLogger = (): Logger => logger; + const ccrEnabled = false; + (getPreparedAlert as jest.Mock).mockImplementation(() => { + if (preparedAlertResponse !== undefined) { + return preparedAlertResponse; + } - it('should log a warning if no email is provided', async () => { - const customGetUiSettingsService: any = () => ({ - asScopedToClient: () => ({ - get: () => null, - }), + return { + emailAddress, + data: [license], + clusters: [cluster], + dateFormat, + }; }); - const alert = getLicenseExpiration( - customGetUiSettingsService, - monitoringCluster, - getLogger, - ccrEnabled - ); - const services = { - callCluster: jest.fn( - (method: string, { filterPath }): Promise => { - return new Promise(resolve => { - if (filterPath.includes('hits.hits._source.license.*')) { - resolve( - fillLicense({ - status: 'good', - type: 'basic', - expiry_date_in_millis: moment() - .add(7, 'days') - .valueOf(), - }) - ); - } - resolve({}); - }); - } - ), - alertInstanceFactory: jest.fn(), - savedObjectsClient: savedObjectsClientMock.create(), + const alert = getLicenseExpiration(null as any, null as any, getLogger, ccrEnabled); + const state: AlertCommonState = { + [clusterUuid]: { + expiredCheckDateMS, + ui: { ...defaultUiState }, + } as AlertLicensePerClusterState, }; - const state = {}; + return (await alert.executor({ services, params, state } as any)) as AlertCommonState; + } - await alert.executor({ - ...alertExecutorOptions, - services, - params, - state, - }); - - expect((logger.warn as jest.Mock).mock.calls.length).toBe(1); - expect(logger.warn).toHaveBeenCalledWith( - `Unable to send email for ${ALERT_TYPE_LICENSE_EXPIRATION} because there is no email configured.` - ); + afterEach(() => { + (executeActions as jest.Mock).mockClear(); + (getPreparedAlert as jest.Mock).mockClear(); }); - it('should fire actions if going to expire', async () => { - const scheduleActions = jest.fn(); - const alertInstanceFactory = jest.fn( - (id: string): AlertInstance => { - const instance = new AlertInstance(); - instance.scheduleActions = scheduleActions; - return instance; - } - ); + it('should have the right id and actionGroups', () => { + const alert = getLicenseExpiration(null as any, null as any, jest.fn(), false); + expect(alert.id).toBe(ALERT_TYPE_LICENSE_EXPIRATION); + expect(alert.actionGroups).toEqual([{ id: 'default', name: 'Default' }]); + }); - const alert = getLicenseExpiration( - getUiSettingsService, - monitoringCluster, - getLogger, - ccrEnabled - ); + it('should return the state if no license is provided', async () => { + const result = await setupAlert(null, 0, null); + expect(result[clusterUuid].ui).toEqual(defaultUiState); + }); - const savedObjectsClient = savedObjectsClientMock.create(); - savedObjectsClient.get.mockReturnValue( - new Promise(resolve => { - const savedObject: SavedObject = { - id: '', - type: '', - references: [], - attributes: { - [MONITORING_CONFIG_ALERTING_EMAIL_ADDRESS]: emailAddress, - }, - }; - resolve(savedObject); - }) - ); - const services = { - callCluster: jest.fn( - (method: string, { filterPath }): Promise => { - return new Promise(resolve => { - if (filterPath.includes('hits.hits._source.license.*')) { - resolve( - fillLicense( - { - status: 'active', - type: 'gold', - expiry_date_in_millis: moment() - .add(7, 'days') - .valueOf(), - }, - clusterUuid - ) - ); - } - resolve({}); - }); - } - ), - alertInstanceFactory, - savedObjectsClient, + it('should fire actions if going to expire', async () => { + const expiryDateMS = moment() + .add(7, 'days') + .valueOf(); + const license = { + status: 'active', + type: 'gold', + expiryDateMS, + clusterUuid, }; - - const state = {}; - - const result: AlertState = (await alert.executor({ - ...alertExecutorOptions, - services, - params, - state, - })) as AlertState; - - const newState: AlertClusterState = result[clusterUuid] as AlertClusterState; - + const result = await setupAlert(license, 0); + const newState = result[clusterUuid] as AlertLicensePerClusterState; expect(newState.expiredCheckDateMS > 0).toBe(true); - expect(scheduleActions.mock.calls.length).toBe(1); - expect(scheduleActions.mock.calls[0][1].subject).toBe( - 'NEW X-Pack Monitoring: License Expiration' + expect(executeActions).toHaveBeenCalledWith( + undefined, + cluster, + moment.utc(expiryDateMS), + dateFormat, + emailAddress ); - expect(scheduleActions.mock.calls[0][1].to).toBe(emailAddress); }); it('should fire actions if the user fixed their license', async () => { - const scheduleActions = jest.fn(); - const alertInstanceFactory = jest.fn( - (id: string): AlertInstance => { - const instance = new AlertInstance(); - instance.scheduleActions = scheduleActions; - return instance; - } - ); - const alert = getLicenseExpiration( - getUiSettingsService, - monitoringCluster, - getLogger, - ccrEnabled - ); - - const savedObjectsClient = savedObjectsClientMock.create(); - savedObjectsClient.get.mockReturnValue( - new Promise(resolve => { - const savedObject: SavedObject = { - id: '', - type: '', - references: [], - attributes: { - [MONITORING_CONFIG_ALERTING_EMAIL_ADDRESS]: emailAddress, - }, - }; - resolve(savedObject); - }) - ); - const services = { - callCluster: jest.fn( - (method: string, { filterPath }): Promise => { - return new Promise(resolve => { - if (filterPath.includes('hits.hits._source.license.*')) { - resolve( - fillLicense( - { - status: 'active', - type: 'gold', - expiry_date_in_millis: moment() - .add(120, 'days') - .valueOf(), - }, - clusterUuid - ) - ); - } - resolve({}); - }); - } - ), - alertInstanceFactory, - savedObjectsClient, - }; - - const state: AlertState = { - [clusterUuid]: { - expiredCheckDateMS: moment() - .subtract(1, 'day') - .valueOf(), - ui: { isFiring: true, severity: 0, message: null, resolvedMS: 0, expirationTime: 0 }, - }, + const expiryDateMS = moment() + .add(365, 'days') + .valueOf(); + const license = { + status: 'active', + type: 'gold', + expiryDateMS, + clusterUuid, }; - - const result: AlertState = (await alert.executor({ - ...alertExecutorOptions, - services, - params, - state, - })) as AlertState; - - const newState: AlertClusterState = result[clusterUuid] as AlertClusterState; + const result = await setupAlert(license, 100); + const newState = result[clusterUuid] as AlertLicensePerClusterState; expect(newState.expiredCheckDateMS).toBe(0); - expect(scheduleActions.mock.calls.length).toBe(1); - expect(scheduleActions.mock.calls[0][1].subject).toBe( - 'RESOLVED X-Pack Monitoring: License Expiration' + expect(executeActions).toHaveBeenCalledWith( + undefined, + cluster, + moment.utc(expiryDateMS), + dateFormat, + emailAddress, + true ); - expect(scheduleActions.mock.calls[0][1].to).toBe(emailAddress); }); it('should not fire actions for trial license that expire in more than 14 days', async () => { - const scheduleActions = jest.fn(); - const alertInstanceFactory = jest.fn( - (id: string): AlertInstance => { - const instance = new AlertInstance(); - instance.scheduleActions = scheduleActions; - return instance; - } - ); - const alert = getLicenseExpiration( - getUiSettingsService, - monitoringCluster, - getLogger, - ccrEnabled - ); - - const savedObjectsClient = savedObjectsClientMock.create(); - savedObjectsClient.get.mockReturnValue( - new Promise(resolve => { - const savedObject: SavedObject = { - id: '', - type: '', - references: [], - attributes: { - [MONITORING_CONFIG_ALERTING_EMAIL_ADDRESS]: emailAddress, - }, - }; - resolve(savedObject); - }) - ); - const services = { - callCluster: jest.fn( - (method: string, { filterPath }): Promise => { - return new Promise(resolve => { - if (filterPath.includes('hits.hits._source.license.*')) { - resolve( - fillLicense( - { - status: 'active', - type: 'trial', - expiry_date_in_millis: moment() - .add(15, 'days') - .valueOf(), - }, - clusterUuid - ) - ); - } - resolve({}); - }); - } - ), - alertInstanceFactory, - savedObjectsClient, + const expiryDateMS = moment() + .add(20, 'days') + .valueOf(); + const license = { + status: 'active', + type: 'trial', + expiryDateMS, + clusterUuid, }; - - const state = {}; - const result: AlertState = (await alert.executor({ - ...alertExecutorOptions, - services, - params, - state, - })) as AlertState; - - const newState: AlertClusterState = result[clusterUuid] as AlertClusterState; - expect(newState.expiredCheckDateMS).toBe(undefined); - expect(scheduleActions).not.toHaveBeenCalled(); + const result = await setupAlert(license, 0); + const newState = result[clusterUuid] as AlertLicensePerClusterState; + expect(newState.expiredCheckDateMS).toBe(0); + expect(executeActions).not.toHaveBeenCalled(); }); it('should fire actions for trial license that in 14 days or less', async () => { - const scheduleActions = jest.fn(); - const alertInstanceFactory = jest.fn( - (id: string): AlertInstance => { - const instance = new AlertInstance(); - instance.scheduleActions = scheduleActions; - return instance; - } - ); - const alert = getLicenseExpiration( - getUiSettingsService, - monitoringCluster, - getLogger, - ccrEnabled - ); - - const savedObjectsClient = savedObjectsClientMock.create(); - savedObjectsClient.get.mockReturnValue( - new Promise(resolve => { - const savedObject: SavedObject = { - id: '', - type: '', - references: [], - attributes: { - [MONITORING_CONFIG_ALERTING_EMAIL_ADDRESS]: emailAddress, - }, - }; - resolve(savedObject); - }) - ); - const services = { - callCluster: jest.fn( - (method: string, { filterPath }): Promise => { - return new Promise(resolve => { - if (filterPath.includes('hits.hits._source.license.*')) { - resolve( - fillLicense( - { - status: 'active', - type: 'trial', - expiry_date_in_millis: moment() - .add(13, 'days') - .valueOf(), - }, - clusterUuid - ) - ); - } - resolve({}); - }); - } - ), - alertInstanceFactory, - savedObjectsClient, + const expiryDateMS = moment() + .add(7, 'days') + .valueOf(); + const license = { + status: 'active', + type: 'trial', + expiryDateMS, + clusterUuid, }; - - const state = {}; - const result: AlertState = (await alert.executor({ - ...alertExecutorOptions, - services, - params, - state, - })) as AlertState; - - const newState: AlertClusterState = result[clusterUuid] as AlertClusterState; + const result = await setupAlert(license, 0); + const newState = result[clusterUuid] as AlertLicensePerClusterState; expect(newState.expiredCheckDateMS > 0).toBe(true); - expect(scheduleActions.mock.calls.length).toBe(1); + expect(executeActions).toHaveBeenCalledWith( + undefined, + cluster, + moment.utc(expiryDateMS), + dateFormat, + emailAddress + ); }); }); diff --git a/x-pack/plugins/monitoring/server/alerts/license_expiration.ts b/x-pack/plugins/monitoring/server/alerts/license_expiration.ts index 93397ff3641ae..2e5356150086b 100644 --- a/x-pack/plugins/monitoring/server/alerts/license_expiration.ts +++ b/x-pack/plugins/monitoring/server/alerts/license_expiration.ts @@ -5,24 +5,20 @@ */ import moment from 'moment-timezone'; -import { get } from 'lodash'; import { Logger, ICustomClusterClient, UiSettingsServiceStart } from 'src/core/server'; import { i18n } from '@kbn/i18n'; -import { ALERT_TYPE_LICENSE_EXPIRATION, INDEX_PATTERN_ELASTICSEARCH } from '../../common/constants'; +import { ALERT_TYPE_LICENSE_EXPIRATION } from '../../common/constants'; import { AlertType } from '../../../../plugins/alerting/server'; import { fetchLicenses } from '../lib/alerts/fetch_licenses'; -import { fetchDefaultEmailAddress } from '../lib/alerts/fetch_default_email_address'; -import { fetchClusters } from '../lib/alerts/fetch_clusters'; -import { fetchAvailableCcs } from '../lib/alerts/fetch_available_ccs'; import { - AlertLicense, - AlertState, - AlertClusterState, - AlertClusterUiState, - LicenseExpirationAlertExecutorOptions, + AlertCommonState, + AlertLicensePerClusterState, + AlertCommonExecutorOptions, + AlertCommonCluster, + AlertLicensePerClusterUiState, } from './types'; -import { getCcsIndexPattern } from '../lib/alerts/get_ccs_index_pattern'; import { executeActions, getUiMessage } from '../lib/alerts/license_expiration.lib'; +import { getPreparedAlert } from '../lib/alerts/get_prepared_alert'; const EXPIRES_DAYS = [60, 30, 14, 7]; @@ -32,14 +28,6 @@ export const getLicenseExpiration = ( getLogger: (...scopes: string[]) => Logger, ccsEnabled: boolean ): AlertType => { - async function getCallCluster(services: any): Promise { - if (!monitoringCluster) { - return services.callCluster; - } - - return monitoringCluster.callAsInternalUser; - } - const logger = getLogger(ALERT_TYPE_LICENSE_EXPIRATION); return { id: ALERT_TYPE_LICENSE_EXPIRATION, @@ -53,54 +41,50 @@ export const getLicenseExpiration = ( }, ], defaultActionGroupId: 'default', - async executor({ - services, - params, - state, - }: LicenseExpirationAlertExecutorOptions): Promise { + async executor({ services, params, state }: AlertCommonExecutorOptions): Promise { logger.debug( `Firing alert with params: ${JSON.stringify(params)} and state: ${JSON.stringify(state)}` ); - const callCluster = await getCallCluster(services); - - // Support CCS use cases by querying to find available remote clusters - // and then adding those to the index pattern we are searching against - let esIndexPattern = INDEX_PATTERN_ELASTICSEARCH; - if (ccsEnabled) { - const availableCcs = await fetchAvailableCcs(callCluster); - if (availableCcs.length > 0) { - esIndexPattern = getCcsIndexPattern(esIndexPattern, availableCcs); - } - } - - const clusters = await fetchClusters(callCluster, esIndexPattern); + const preparedAlert = await getPreparedAlert( + ALERT_TYPE_LICENSE_EXPIRATION, + getUiSettingsService, + monitoringCluster, + logger, + ccsEnabled, + services, + fetchLicenses + ); - // Fetch licensing information from cluster_stats documents - const licenses: AlertLicense[] = await fetchLicenses(callCluster, clusters, esIndexPattern); - if (licenses.length === 0) { - logger.warn(`No license found for ${ALERT_TYPE_LICENSE_EXPIRATION}.`); + if (!preparedAlert) { return state; } - const uiSettings = (await getUiSettingsService()).asScopedToClient( - services.savedObjectsClient - ); - const dateFormat: string = await uiSettings.get('dateFormat'); - const timezone: string = await uiSettings.get('dateFormat:tz'); - const emailAddress = await fetchDefaultEmailAddress(uiSettings); - if (!emailAddress) { - // TODO: we can do more here - logger.warn( - `Unable to send email for ${ALERT_TYPE_LICENSE_EXPIRATION} because there is no email configured.` - ); - return; - } + const { emailAddress, data: licenses, clusters, dateFormat } = preparedAlert; - const result: AlertState = { ...state }; + const result: AlertCommonState = { ...state }; + const defaultAlertState: AlertLicensePerClusterState = { + expiredCheckDateMS: 0, + ui: { + isFiring: false, + message: null, + severity: 0, + resolvedMS: 0, + lastCheckedMS: 0, + triggeredMS: 0, + }, + }; for (const license of licenses) { - const licenseState: AlertClusterState = state[license.clusterUuid] || {}; + const alertState: AlertLicensePerClusterState = + (state[license.clusterUuid] as AlertLicensePerClusterState) || defaultAlertState; + const cluster = clusters.find( + (c: AlertCommonCluster) => c.clusterUuid === license.clusterUuid + ); + if (!cluster) { + logger.warn(`Unable to find cluster for clusterUuid='${license.clusterUuid}'`); + continue; + } const $expiry = moment.utc(license.expiryDateMS); let isExpired = false; let severity = 0; @@ -123,31 +107,26 @@ export const getLicenseExpiration = ( } } - const ui: AlertClusterUiState = get(licenseState, 'ui', { - isFiring: false, - message: null, - severity: 0, - resolvedMS: 0, - expirationTime: 0, - }); + const ui = alertState.ui; + let triggered = ui.triggeredMS; let resolved = ui.resolvedMS; let message = ui.message; - let expiredCheckDate = licenseState.expiredCheckDateMS; + let expiredCheckDate = alertState.expiredCheckDateMS; const instance = services.alertInstanceFactory(ALERT_TYPE_LICENSE_EXPIRATION); if (isExpired) { - if (!licenseState.expiredCheckDateMS) { + if (!alertState.expiredCheckDateMS) { logger.debug(`License will expire soon, sending email`); - executeActions(instance, license, $expiry, dateFormat, emailAddress); - expiredCheckDate = moment().valueOf(); + executeActions(instance, cluster, $expiry, dateFormat, emailAddress); + expiredCheckDate = triggered = moment().valueOf(); } - message = getUiMessage(license, timezone); + message = getUiMessage(); resolved = 0; - } else if (!isExpired && licenseState.expiredCheckDateMS) { + } else if (!isExpired && alertState.expiredCheckDateMS) { logger.debug(`License expiration has been resolved, sending email`); - executeActions(instance, license, $expiry, dateFormat, emailAddress, true); + executeActions(instance, cluster, $expiry, dateFormat, emailAddress, true); expiredCheckDate = 0; - message = getUiMessage(license, timezone, true); + message = getUiMessage(true); resolved = moment().valueOf(); } @@ -159,8 +138,10 @@ export const getLicenseExpiration = ( isFiring: expiredCheckDate > 0, severity, resolvedMS: resolved, - }, - }; + triggeredMS: triggered, + lastCheckedMS: moment().valueOf(), + } as AlertLicensePerClusterUiState, + } as AlertLicensePerClusterState; } return result; diff --git a/x-pack/plugins/monitoring/server/alerts/types.d.ts b/x-pack/plugins/monitoring/server/alerts/types.d.ts index ff47d6f2ad4dc..b689d008b51a7 100644 --- a/x-pack/plugins/monitoring/server/alerts/types.d.ts +++ b/x-pack/plugins/monitoring/server/alerts/types.d.ts @@ -5,41 +5,79 @@ */ import { Moment } from 'moment'; import { AlertExecutorOptions } from '../../../alerting/server'; +import { AlertClusterStateState, AlertCommonPerClusterMessageTokenType } from './enums'; export interface AlertLicense { status: string; type: string; expiryDateMS: number; clusterUuid: string; - clusterName: string; } -export interface AlertState { - [clusterUuid: string]: AlertClusterState; +export interface AlertClusterState { + state: AlertClusterStateState; + clusterUuid: string; +} + +export interface AlertCommonState { + [clusterUuid: string]: AlertCommonPerClusterState; } -export interface AlertClusterState { - expiredCheckDateMS: number | Moment; - ui: AlertClusterUiState; +export interface AlertCommonPerClusterState { + ui: AlertCommonPerClusterUiState; } -export interface AlertClusterUiState { +export interface AlertClusterStatePerClusterState extends AlertCommonPerClusterState { + state: AlertClusterStateState; +} + +export interface AlertLicensePerClusterState extends AlertCommonPerClusterState { + expiredCheckDateMS: number; +} + +export interface AlertCommonPerClusterUiState { isFiring: boolean; severity: number; - message: string | null; + message: AlertCommonPerClusterMessage | null; resolvedMS: number; + lastCheckedMS: number; + triggeredMS: number; +} + +export interface AlertCommonPerClusterMessage { + text: string; // Do this. #link this is a link #link + tokens?: AlertCommonPerClusterMessageToken[]; +} + +export interface AlertCommonPerClusterMessageToken { + startToken: string; + endToken?: string; + type: AlertCommonPerClusterMessageTokenType; +} + +export interface AlertCommonPerClusterMessageLinkToken extends AlertCommonPerClusterMessageToken { + url?: string; +} + +export interface AlertCommonPerClusterMessageTimeToken extends AlertCommonPerClusterMessageToken { + isRelative: boolean; + isAbsolute: boolean; +} + +export interface AlertLicensePerClusterUiState extends AlertCommonPerClusterUiState { expirationTime: number; } -export interface AlertCluster { +export interface AlertCommonCluster { clusterUuid: string; + clusterName: string; } -export interface LicenseExpirationAlertExecutorOptions extends AlertExecutorOptions { - state: AlertState; +export interface AlertCommonExecutorOptions extends AlertExecutorOptions { + state: AlertCommonState; } -export interface AlertParams { +export interface AlertCommonParams { dateFormat: string; timezone: string; } diff --git a/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/get_kibana_usage_collector.ts b/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/get_kibana_usage_collector.ts deleted file mode 100644 index 2c40ac56e19ec..0000000000000 --- a/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/get_kibana_usage_collector.ts +++ /dev/null @@ -1,86 +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, snakeCase } from 'lodash'; -import { CallCluster } from 'src/legacy/core_plugins/elasticsearch'; -import { KIBANA_USAGE_TYPE, KIBANA_STATS_TYPE_MONITORING } from '../../../common/constants'; - -const TYPES = [ - 'dashboard', - 'visualization', - 'search', - 'index-pattern', - 'graph-workspace', - 'timelion-sheet', -]; - -/** - * Fetches saved object counts by querying the .kibana index - */ -export function getKibanaUsageCollector(usageCollection: any, kibanaIndex: string) { - return usageCollection.makeUsageCollector({ - type: KIBANA_USAGE_TYPE, - isReady: () => true, - async fetch(callCluster: CallCluster) { - const savedObjectCountSearchParams = { - index: kibanaIndex, - ignoreUnavailable: true, - filterPath: 'aggregations.types.buckets', - body: { - size: 0, - query: { - terms: { type: TYPES }, - }, - aggs: { - types: { - terms: { field: 'type', size: TYPES.length }, - }, - }, - }, - }; - - const resp = await callCluster('search', savedObjectCountSearchParams); - const buckets: any = get(resp, 'aggregations.types.buckets', []); - - // get the doc_count from each bucket - const bucketCounts = buckets.reduce( - (acc: any, bucket: any) => ({ - ...acc, - [bucket.key]: bucket.doc_count, - }), - {} - ); - - return { - index: kibanaIndex, - ...TYPES.reduce( - (acc, type) => ({ - // combine the bucketCounts and 0s for types that don't have documents - ...acc, - [snakeCase(type)]: { - total: bucketCounts[type] || 0, - }, - }), - {} - ), - }; - }, - - /* - * Format the response data into a model for internal upload - * 1. Make this data part of the "kibana_stats" type - * 2. Organize the payload in the usage namespace of the data payload (usage.index, etc) - */ - formatForBulkUpload: (result: any) => { - return { - type: KIBANA_STATS_TYPE_MONITORING, - payload: { - usage: result, - }, - }; - }, - }); -} diff --git a/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/get_ops_stats_collector.ts b/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/get_ops_stats_collector.ts deleted file mode 100644 index 85357f786ddc1..0000000000000 --- a/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/get_ops_stats_collector.ts +++ /dev/null @@ -1,46 +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 { Observable } from 'rxjs'; -import { cloneDeep } from 'lodash'; -import moment from 'moment'; -import { OpsMetrics } from 'kibana/server'; -import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; -import { KIBANA_STATS_TYPE_MONITORING } from '../../../common/constants'; - -interface MonitoringOpsMetrics extends OpsMetrics { - timestamp: string; -} - -/* - * Initialize a collector for Kibana Ops Stats - */ -export function getOpsStatsCollector( - usageCollection: UsageCollectionSetup, - metrics$: Observable -) { - let lastMetrics: MonitoringOpsMetrics | null = null; - metrics$.subscribe(_metrics => { - const metrics: any = cloneDeep(_metrics); - // Ensure we only include the same data that Metricbeat collection would get - delete metrics.process.pid; - metrics.response_times = { - average: metrics.response_times.avg_in_millis, - max: metrics.response_times.max_in_millis, - }; - delete metrics.requests.statusCodes; - lastMetrics = { - ...metrics, - timestamp: moment.utc().toISOString(), - }; - }); - - return usageCollection.makeStatsCollector({ - type: KIBANA_STATS_TYPE_MONITORING, - isReady: () => !!lastMetrics, - fetch: () => lastMetrics, - }); -} diff --git a/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/index.ts b/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/index.ts index e41b1512f1b29..dcd35b0d323eb 100644 --- a/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/index.ts +++ b/x-pack/plugins/monitoring/server/kibana_monitoring/collectors/index.ts @@ -3,20 +3,14 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { Observable } from 'rxjs'; -import { OpsMetrics } from 'kibana/server'; -import { getKibanaUsageCollector } from './get_kibana_usage_collector'; -import { getOpsStatsCollector } from './get_ops_stats_collector'; + +import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { getSettingsCollector } from './get_settings_collector'; import { MonitoringConfig } from '../../config'; export function registerCollectors( - usageCollection: any, - config: MonitoringConfig, - opsMetrics$: Observable, - kibanaIndex: string + usageCollection: UsageCollectionSetup, + config: MonitoringConfig ) { - usageCollection.registerCollector(getOpsStatsCollector(usageCollection, opsMetrics$)); - usageCollection.registerCollector(getKibanaUsageCollector(usageCollection, kibanaIndex)); usageCollection.registerCollector(getSettingsCollector(usageCollection, config)); } diff --git a/x-pack/plugins/monitoring/server/lib/alerts/cluster_state.lib.test.ts b/x-pack/plugins/monitoring/server/lib/alerts/cluster_state.lib.test.ts new file mode 100644 index 0000000000000..81e375734cc50 --- /dev/null +++ b/x-pack/plugins/monitoring/server/lib/alerts/cluster_state.lib.test.ts @@ -0,0 +1,70 @@ +/* + * 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 { executeActions, getUiMessage } from './cluster_state.lib'; +import { AlertClusterStateState } from '../../alerts/enums'; +import { AlertCommonPerClusterMessageLinkToken } from '../../alerts/types'; + +describe('clusterState lib', () => { + describe('executeActions', () => { + const clusterName = 'clusterA'; + const instance: any = { scheduleActions: jest.fn() }; + const license: any = { clusterName }; + const status = AlertClusterStateState.Green; + const emailAddress = 'test@test.com'; + + beforeEach(() => { + instance.scheduleActions.mockClear(); + }); + + it('should schedule actions when firing', () => { + executeActions(instance, license, status, emailAddress, false); + expect(instance.scheduleActions).toHaveBeenCalledWith('default', { + subject: 'NEW X-Pack Monitoring: Cluster Status', + message: `Allocate missing replica shards for cluster '${clusterName}'`, + to: emailAddress, + }); + }); + + it('should have a different message for red state', () => { + executeActions(instance, license, AlertClusterStateState.Red, emailAddress, false); + expect(instance.scheduleActions).toHaveBeenCalledWith('default', { + subject: 'NEW X-Pack Monitoring: Cluster Status', + message: `Allocate missing primary and replica shards for cluster '${clusterName}'`, + to: emailAddress, + }); + }); + + it('should schedule actions when resolved', () => { + executeActions(instance, license, status, emailAddress, true); + expect(instance.scheduleActions).toHaveBeenCalledWith('default', { + subject: 'RESOLVED X-Pack Monitoring: Cluster Status', + message: `This cluster alert has been resolved: Allocate missing replica shards for cluster '${clusterName}'`, + to: emailAddress, + }); + }); + }); + + describe('getUiMessage', () => { + it('should return a message when firing', () => { + const message = getUiMessage(AlertClusterStateState.Red, false); + expect(message.text).toBe( + `Elasticsearch cluster status is red. #start_linkAllocate missing primary and replica shards#end_link` + ); + expect(message.tokens && message.tokens.length).toBe(1); + expect(message.tokens && message.tokens[0].startToken).toBe('#start_link'); + expect(message.tokens && message.tokens[0].endToken).toBe('#end_link'); + expect( + message.tokens && (message.tokens[0] as AlertCommonPerClusterMessageLinkToken).url + ).toBe('elasticsearch/indices'); + }); + + it('should return a message when resolved', () => { + const message = getUiMessage(AlertClusterStateState.Green, true); + expect(message.text).toBe(`Elasticsearch cluster status is green.`); + expect(message.tokens).not.toBeDefined(); + }); + }); +}); diff --git a/x-pack/plugins/monitoring/server/lib/alerts/cluster_state.lib.ts b/x-pack/plugins/monitoring/server/lib/alerts/cluster_state.lib.ts new file mode 100644 index 0000000000000..ae66d603507ca --- /dev/null +++ b/x-pack/plugins/monitoring/server/lib/alerts/cluster_state.lib.ts @@ -0,0 +1,88 @@ +/* + * 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 { AlertInstance } from '../../../../alerting/server'; +import { + AlertCommonCluster, + AlertCommonPerClusterMessage, + AlertCommonPerClusterMessageLinkToken, +} from '../../alerts/types'; +import { AlertClusterStateState, AlertCommonPerClusterMessageTokenType } from '../../alerts/enums'; + +const RESOLVED_SUBJECT = i18n.translate('xpack.monitoring.alerts.clusterStatus.resolvedSubject', { + defaultMessage: 'RESOLVED X-Pack Monitoring: Cluster Status', +}); + +const NEW_SUBJECT = i18n.translate('xpack.monitoring.alerts.clusterStatus.newSubject', { + defaultMessage: 'NEW X-Pack Monitoring: Cluster Status', +}); + +const RED_STATUS_MESSAGE = i18n.translate('xpack.monitoring.alerts.clusterStatus.redMessage', { + defaultMessage: 'Allocate missing primary and replica shards', +}); + +const YELLOW_STATUS_MESSAGE = i18n.translate( + 'xpack.monitoring.alerts.clusterStatus.yellowMessage', + { + defaultMessage: 'Allocate missing replica shards', + } +); + +export function executeActions( + instance: AlertInstance, + cluster: AlertCommonCluster, + status: AlertClusterStateState, + emailAddress: string, + resolved: boolean = false +) { + const message = + status === AlertClusterStateState.Red ? RED_STATUS_MESSAGE : YELLOW_STATUS_MESSAGE; + if (resolved) { + instance.scheduleActions('default', { + subject: RESOLVED_SUBJECT, + message: `This cluster alert has been resolved: ${message} for cluster '${cluster.clusterName}'`, + to: emailAddress, + }); + } else { + instance.scheduleActions('default', { + subject: NEW_SUBJECT, + message: `${message} for cluster '${cluster.clusterName}'`, + to: emailAddress, + }); + } +} + +export function getUiMessage( + status: AlertClusterStateState, + resolved: boolean = false +): AlertCommonPerClusterMessage { + if (resolved) { + return { + text: i18n.translate('xpack.monitoring.alerts.clusterStatus.ui.resolvedMessage', { + defaultMessage: `Elasticsearch cluster status is green.`, + }), + }; + } + const message = + status === AlertClusterStateState.Red ? RED_STATUS_MESSAGE : YELLOW_STATUS_MESSAGE; + return { + text: i18n.translate('xpack.monitoring.alerts.clusterStatus.ui.firingMessage', { + defaultMessage: `Elasticsearch cluster status is {status}. #start_link{message}#end_link`, + values: { + status, + message, + }, + }), + tokens: [ + { + startToken: '#start_link', + endToken: '#end_link', + type: AlertCommonPerClusterMessageTokenType.Link, + url: 'elasticsearch/indices', + } as AlertCommonPerClusterMessageLinkToken, + ], + }; +} diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_cluster_state.test.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_cluster_state.test.ts new file mode 100644 index 0000000000000..642ae3c39a027 --- /dev/null +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_cluster_state.test.ts @@ -0,0 +1,39 @@ +/* + * 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 { fetchClusterState } from './fetch_cluster_state'; + +describe('fetchClusterState', () => { + it('should return the cluster state', async () => { + const status = 'green'; + const clusterUuid = 'sdfdsaj34434'; + const callCluster = jest.fn(() => ({ + hits: { + hits: [ + { + _source: { + cluster_state: { + status, + }, + cluster_uuid: clusterUuid, + }, + }, + ], + }, + })); + + const clusters = [{ clusterUuid, clusterName: 'foo' }]; + const index = '.monitoring-es-*'; + + const state = await fetchClusterState(callCluster, clusters, index); + expect(state).toEqual([ + { + state: status, + clusterUuid, + }, + ]); + }); +}); diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_cluster_state.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_cluster_state.ts new file mode 100644 index 0000000000000..66ea30d5f2e96 --- /dev/null +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_cluster_state.ts @@ -0,0 +1,53 @@ +/* + * 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 } from 'lodash'; +import { AlertCommonCluster, AlertClusterState } from '../../alerts/types'; + +export async function fetchClusterState( + callCluster: any, + clusters: AlertCommonCluster[], + index: string +): Promise { + const params = { + index, + filterPath: ['hits.hits._source.cluster_state.status', 'hits.hits._source.cluster_uuid'], + body: { + size: 1, + sort: [{ timestamp: { order: 'desc' } }], + query: { + bool: { + filter: [ + { + terms: { + cluster_uuid: clusters.map(cluster => cluster.clusterUuid), + }, + }, + { + term: { + type: 'cluster_stats', + }, + }, + { + range: { + timestamp: { + gte: 'now-2m', + }, + }, + }, + ], + }, + }, + }, + }; + + const response = await callCluster('search', params); + return get(response, 'hits.hits', []).map((hit: any) => { + return { + state: get(hit, '_source.cluster_state.status'), + clusterUuid: get(hit, '_source.cluster_uuid'), + }; + }); +} diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_clusters.test.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_clusters.test.ts index 78eb9773df15f..7a9b61f37707b 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_clusters.test.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_clusters.test.ts @@ -6,21 +6,51 @@ import { fetchClusters } from './fetch_clusters'; describe('fetchClusters', () => { + const clusterUuid = '1sdfds734'; + const clusterName = 'monitoring'; + it('return a list of clusters', async () => { const callCluster = jest.fn().mockImplementation(() => ({ - aggregations: { - clusters: { - buckets: [ - { - key: 'clusterA', + hits: { + hits: [ + { + _source: { + cluster_uuid: clusterUuid, + cluster_name: clusterName, + }, + }, + ], + }, + })); + const index = '.monitoring-es-*'; + const result = await fetchClusters(callCluster, index); + expect(result).toEqual([{ clusterUuid, clusterName }]); + }); + + it('return the metadata name if available', async () => { + const metadataName = 'custom-monitoring'; + const callCluster = jest.fn().mockImplementation(() => ({ + hits: { + hits: [ + { + _source: { + cluster_uuid: clusterUuid, + cluster_name: clusterName, + cluster_settings: { + cluster: { + metadata: { + display_name: metadataName, + }, + }, + }, }, - ], - }, + }, + ], }, })); const index = '.monitoring-es-*'; const result = await fetchClusters(callCluster, index); - expect(result).toEqual([{ clusterUuid: 'clusterA' }]); + expect(result).toEqual([{ clusterUuid, clusterName: metadataName }]); }); it('should limit the time period in the query', async () => { diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_clusters.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_clusters.ts index 8ef7339618a2c..d1513ac16fb15 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_clusters.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_clusters.ts @@ -4,18 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ import { get } from 'lodash'; -import { AlertCluster } from '../../alerts/types'; +import { AlertCommonCluster } from '../../alerts/types'; -interface AggregationResult { - key: string; -} - -export async function fetchClusters(callCluster: any, index: string): Promise { +export async function fetchClusters( + callCluster: any, + index: string +): Promise { const params = { index, - filterPath: 'aggregations.clusters.buckets', + filterPath: [ + 'hits.hits._source.cluster_settings.cluster.metadata.display_name', + 'hits.hits._source.cluster_uuid', + 'hits.hits._source.cluster_name', + ], body: { - size: 0, + size: 1000, query: { bool: { filter: [ @@ -34,19 +37,21 @@ export async function fetchClusters(callCluster: any, index: string): Promise ({ - clusterUuid: bucket.key, - })); + return get(response, 'hits.hits', []).map((hit: any) => { + const clusterName: string = + get(hit, '_source.cluster_settings.cluster.metadata.display_name') || + get(hit, '_source.cluster_name') || + get(hit, '_source.cluster_uuid'); + return { + clusterUuid: get(hit, '_source.cluster_uuid'), + clusterName, + }; + }); } diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_licenses.test.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_licenses.test.ts index dd6c074e68b1f..9dcb4ffb82a5f 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_licenses.test.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_licenses.test.ts @@ -6,28 +6,28 @@ import { fetchLicenses } from './fetch_licenses'; describe('fetchLicenses', () => { + const clusterName = 'MyCluster'; + const clusterUuid = 'clusterA'; + const license = { + status: 'active', + expiry_date_in_millis: 1579532493876, + type: 'basic', + }; + it('return a list of licenses', async () => { - const clusterName = 'MyCluster'; - const clusterUuid = 'clusterA'; - const license = { - status: 'active', - expiry_date_in_millis: 1579532493876, - type: 'basic', - }; const callCluster = jest.fn().mockImplementation(() => ({ hits: { hits: [ { _source: { license, - cluster_name: clusterName, cluster_uuid: clusterUuid, }, }, ], }, })); - const clusters = [{ clusterUuid }]; + const clusters = [{ clusterUuid, clusterName }]; const index = '.monitoring-es-*'; const result = await fetchLicenses(callCluster, clusters, index); expect(result).toEqual([ @@ -36,15 +36,13 @@ describe('fetchLicenses', () => { type: license.type, expiryDateMS: license.expiry_date_in_millis, clusterUuid, - clusterName, }, ]); }); it('should only search for the clusters provided', async () => { - const clusterUuid = 'clusterA'; const callCluster = jest.fn(); - const clusters = [{ clusterUuid }]; + const clusters = [{ clusterUuid, clusterName }]; const index = '.monitoring-es-*'; await fetchLicenses(callCluster, clusters, index); const params = callCluster.mock.calls[0][1]; @@ -52,54 +50,11 @@ describe('fetchLicenses', () => { }); it('should limit the time period in the query', async () => { - const clusterUuid = 'clusterA'; const callCluster = jest.fn(); - const clusters = [{ clusterUuid }]; + const clusters = [{ clusterUuid, clusterName }]; const index = '.monitoring-es-*'; await fetchLicenses(callCluster, clusters, index); const params = callCluster.mock.calls[0][1]; expect(params.body.query.bool.filter[2].range.timestamp.gte).toBe('now-2m'); }); - - it('should give priority to the metadata name', async () => { - const clusterName = 'MyCluster'; - const clusterUuid = 'clusterA'; - const license = { - status: 'active', - expiry_date_in_millis: 1579532493876, - type: 'basic', - }; - const callCluster = jest.fn().mockImplementation(() => ({ - hits: { - hits: [ - { - _source: { - license, - cluster_name: 'fakeName', - cluster_uuid: clusterUuid, - cluster_settings: { - cluster: { - metadata: { - display_name: clusterName, - }, - }, - }, - }, - }, - ], - }, - })); - const clusters = [{ clusterUuid }]; - const index = '.monitoring-es-*'; - const result = await fetchLicenses(callCluster, clusters, index); - expect(result).toEqual([ - { - status: license.status, - type: license.type, - expiryDateMS: license.expiry_date_in_millis, - clusterUuid, - clusterName, - }, - ]); - }); }); diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_licenses.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_licenses.ts index 31a68e8aa9c3e..5b05c907e796e 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_licenses.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_licenses.ts @@ -4,21 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ import { get } from 'lodash'; -import { AlertLicense, AlertCluster } from '../../alerts/types'; +import { AlertLicense, AlertCommonCluster } from '../../alerts/types'; export async function fetchLicenses( callCluster: any, - clusters: AlertCluster[], + clusters: AlertCommonCluster[], index: string ): Promise { const params = { index, - filterPath: [ - 'hits.hits._source.license.*', - 'hits.hits._source.cluster_settings.cluster.metadata.display_name', - 'hits.hits._source.cluster_uuid', - 'hits.hits._source.cluster_name', - ], + filterPath: ['hits.hits._source.license.*', 'hits.hits._source.cluster_uuid'], body: { size: 1, sort: [{ timestamp: { order: 'desc' } }], @@ -50,17 +45,12 @@ export async function fetchLicenses( const response = await callCluster('search', params); return get(response, 'hits.hits', []).map((hit: any) => { - const clusterName: string = - get(hit, '_source.cluster_settings.cluster.metadata.display_name') || - get(hit, '_source.cluster_name') || - get(hit, '_source.cluster_uuid'); const rawLicense: any = get(hit, '_source.license', {}); const license: AlertLicense = { status: rawLicense.status, type: rawLicense.type, expiryDateMS: rawLicense.expiry_date_in_millis, clusterUuid: get(hit, '_source.cluster_uuid'), - clusterName, }; return license; }); diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.ts new file mode 100644 index 0000000000000..a3bcb61afacd6 --- /dev/null +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.test.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 { fetchStatus } from './fetch_status'; +import { AlertCommonPerClusterState } from '../../alerts/types'; + +describe('fetchStatus', () => { + const alertType = 'monitoringTest'; + const log = { warn: jest.fn() }; + const start = 0; + const end = 0; + const id = 1; + const defaultUiState = { + isFiring: false, + severity: 0, + message: null, + resolvedMS: 0, + lastCheckedMS: 0, + triggeredMS: 0, + }; + const alertsClient = { + find: jest.fn(() => ({ + total: 1, + data: [ + { + id, + }, + ], + })), + getAlertState: jest.fn(() => ({ + alertTypeState: { + state: { + ui: defaultUiState, + } as AlertCommonPerClusterState, + }, + })), + }; + + afterEach(() => { + (alertsClient.find as jest.Mock).mockClear(); + (alertsClient.getAlertState as jest.Mock).mockClear(); + }); + + it('should fetch from the alerts client', async () => { + const status = await fetchStatus(alertsClient as any, [alertType], start, end, log as any); + expect(status).toEqual([]); + }); + + it('should return alerts that are firing', async () => { + alertsClient.getAlertState = jest.fn(() => ({ + alertTypeState: { + state: { + ui: { + ...defaultUiState, + isFiring: true, + }, + } as AlertCommonPerClusterState, + }, + })); + + const status = await fetchStatus(alertsClient as any, [alertType], start, end, log as any); + expect(status.length).toBe(1); + expect(status[0].type).toBe(alertType); + expect(status[0].isFiring).toBe(true); + }); + + it('should return alerts that have been resolved in the time period', async () => { + alertsClient.getAlertState = jest.fn(() => ({ + alertTypeState: { + state: { + ui: { + ...defaultUiState, + resolvedMS: 1500, + }, + } as AlertCommonPerClusterState, + }, + })); + + const customStart = 1000; + const customEnd = 2000; + + const status = await fetchStatus( + alertsClient as any, + [alertType], + customStart, + customEnd, + log as any + ); + expect(status.length).toBe(1); + expect(status[0].type).toBe(alertType); + expect(status[0].isFiring).toBe(false); + }); + + it('should pass in the right filter to the alerts client', async () => { + await fetchStatus(alertsClient as any, [alertType], start, end, log as any); + expect((alertsClient.find as jest.Mock).mock.calls[0][0].options.filter).toBe( + `alert.attributes.alertTypeId:${alertType}` + ); + }); + + it('should return nothing if no alert state is found', async () => { + alertsClient.getAlertState = jest.fn(() => ({ + alertTypeState: null, + })) as any; + + const status = await fetchStatus(alertsClient as any, [alertType], start, end, log as any); + expect(status).toEqual([]); + }); + + it('should return nothing if no alerts are found', async () => { + alertsClient.find = jest.fn(() => ({ + total: 0, + data: [], + })) as any; + + const status = await fetchStatus(alertsClient as any, [alertType], start, end, log as any); + expect(status).toEqual([]); + }); +}); diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts index 9f7c1d5a994d2..bf6ee965d3b2f 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_status.ts @@ -4,81 +4,53 @@ * you may not use this file except in compliance with the Elastic License. */ import moment from 'moment'; -import { get } from 'lodash'; -import { AlertClusterState } from '../../alerts/types'; -import { ALERT_TYPES, LOGGING_TAG } from '../../../common/constants'; +import { Logger } from '../../../../../../src/core/server'; +import { AlertCommonPerClusterState } from '../../alerts/types'; +import { AlertsClient } from '../../../../alerting/server'; export async function fetchStatus( - callCluster: any, + alertsClient: AlertsClient, + alertTypes: string[], start: number, end: number, - clusterUuid: string, - server: any + log: Logger ): Promise { - // TODO: this shouldn't query task manager directly but rather - // use an api exposed by the alerting/actions plugin - // See https://github.com/elastic/kibana/issues/48442 const statuses = await Promise.all( - ALERT_TYPES.map( + alertTypes.map( type => new Promise(async (resolve, reject) => { - try { - const params = { - index: '.kibana_task_manager', - filterPath: ['hits.hits._source.task.state'], - body: { - size: 1, - sort: [{ updated_at: { order: 'desc' } }], - query: { - bool: { - filter: [ - { - term: { - 'task.taskType': `alerting:${type}`, - }, - }, - ], - }, - }, - }, - }; - - const response = await callCluster('search', params); - const state = get(response, 'hits.hits[0]._source.task.state', '{}'); - const clusterState: AlertClusterState = get( - JSON.parse(state), - `alertTypeState.${clusterUuid}`, - { - expiredCheckDateMS: 0, - ui: { - isFiring: false, - message: null, - severity: 0, - resolvedMS: 0, - expirationTime: 0, - }, - } - ); - const isInBetween = moment(clusterState.ui.resolvedMS).isBetween(start, end); - if (clusterState.ui.isFiring || isInBetween) { - return resolve({ - type, - ...clusterState.ui, - }); - } + // We need to get the id from the alertTypeId + const alerts = await alertsClient.find({ + options: { + filter: `alert.attributes.alertTypeId:${type}`, + }, + }); + if (alerts.total === 0) { return resolve(false); - } catch (err) { - const reason = get(err, 'body.error.type'); - if (reason === 'index_not_found_exception') { - server.log( - ['error', LOGGING_TAG], - `Unable to fetch alerts. Alerts depends on task manager, which has not been started yet.` - ); - } else { - server.log(['error', LOGGING_TAG], err.message); - } + } + + if (alerts.total !== 1) { + log.warn(`Found more than one alert for type ${type} which is unexpected.`); + } + + const id = alerts.data[0].id; + + // Now that we have the id, we can get the state + const states = await alertsClient.getAlertState({ id }); + if (!states || !states.alertTypeState) { + log.warn(`No alert states found for type ${type} which is unexpected.`); return resolve(false); } + + const state = Object.values(states.alertTypeState)[0] as AlertCommonPerClusterState; + const isInBetween = moment(state.ui.resolvedMS).isBetween(start, end); + if (state.ui.isFiring || isInBetween) { + return resolve({ + type, + ...state.ui, + }); + } + return resolve(false); }) ) ); diff --git a/x-pack/plugins/monitoring/server/lib/alerts/get_prepared_alert.test.ts b/x-pack/plugins/monitoring/server/lib/alerts/get_prepared_alert.test.ts new file mode 100644 index 0000000000000..1840a2026a753 --- /dev/null +++ b/x-pack/plugins/monitoring/server/lib/alerts/get_prepared_alert.test.ts @@ -0,0 +1,163 @@ +/* + * 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 { getPreparedAlert } from './get_prepared_alert'; +import { fetchClusters } from './fetch_clusters'; +import { fetchDefaultEmailAddress } from './fetch_default_email_address'; + +jest.mock('./fetch_clusters', () => ({ + fetchClusters: jest.fn(), +})); + +jest.mock('./fetch_default_email_address', () => ({ + fetchDefaultEmailAddress: jest.fn(), +})); + +describe('getPreparedAlert', () => { + const uiSettings = { get: jest.fn() }; + const alertType = 'test'; + const getUiSettingsService = async () => ({ + asScopedToClient: () => uiSettings, + }); + const monitoringCluster = null; + const logger = { warn: jest.fn() }; + const ccsEnabled = false; + const services = { + callCluster: jest.fn(), + savedObjectsClient: null, + }; + const emailAddress = 'foo@foo.com'; + const data = [{ foo: 1 }]; + const dataFetcher = () => data; + const clusterName = 'MonitoringCluster'; + const clusterUuid = 'sdf34sdf'; + const clusters = [{ clusterName, clusterUuid }]; + + afterEach(() => { + (uiSettings.get as jest.Mock).mockClear(); + (services.callCluster as jest.Mock).mockClear(); + (fetchClusters as jest.Mock).mockClear(); + (fetchDefaultEmailAddress as jest.Mock).mockClear(); + }); + + beforeEach(() => { + (fetchClusters as jest.Mock).mockImplementation(() => clusters); + (fetchDefaultEmailAddress as jest.Mock).mockImplementation(() => emailAddress); + }); + + it('should return fields as expected', async () => { + (uiSettings.get as jest.Mock).mockImplementation(() => { + return emailAddress; + }); + + const alert = await getPreparedAlert( + alertType, + getUiSettingsService as any, + monitoringCluster as any, + logger as any, + ccsEnabled, + services as any, + dataFetcher as any + ); + + expect(alert && alert.emailAddress).toBe(emailAddress); + expect(alert && alert.data).toBe(data); + }); + + it('should add ccs if specified', async () => { + const ccsClusterName = 'remoteCluster'; + (services.callCluster as jest.Mock).mockImplementation(() => { + return { + [ccsClusterName]: { + connected: true, + }, + }; + }); + + await getPreparedAlert( + alertType, + getUiSettingsService as any, + monitoringCluster as any, + logger as any, + true, + services as any, + dataFetcher as any + ); + + expect((fetchClusters as jest.Mock).mock.calls[0][1].includes(ccsClusterName)).toBe(true); + }); + + it('should ignore ccs if no remote clusters are available', async () => { + const ccsClusterName = 'remoteCluster'; + (services.callCluster as jest.Mock).mockImplementation(() => { + return { + [ccsClusterName]: { + connected: false, + }, + }; + }); + + await getPreparedAlert( + alertType, + getUiSettingsService as any, + monitoringCluster as any, + logger as any, + true, + services as any, + dataFetcher as any + ); + + expect((fetchClusters as jest.Mock).mock.calls[0][1].includes(ccsClusterName)).toBe(false); + }); + + it('should pass in the clusters into the data fetcher', async () => { + const customDataFetcher = jest.fn(() => data); + + await getPreparedAlert( + alertType, + getUiSettingsService as any, + monitoringCluster as any, + logger as any, + true, + services as any, + customDataFetcher as any + ); + + expect((customDataFetcher as jest.Mock).mock.calls[0][1]).toBe(clusters); + }); + + it('should return nothing if the data fetcher returns nothing', async () => { + const customDataFetcher = jest.fn(() => []); + + const result = await getPreparedAlert( + alertType, + getUiSettingsService as any, + monitoringCluster as any, + logger as any, + true, + services as any, + customDataFetcher as any + ); + + expect(result).toBe(null); + }); + + it('should return nothing if there is no email address', async () => { + (fetchDefaultEmailAddress as jest.Mock).mockImplementation(() => null); + + const result = await getPreparedAlert( + alertType, + getUiSettingsService as any, + monitoringCluster as any, + logger as any, + true, + services as any, + dataFetcher as any + ); + + expect(result).toBe(null); + }); +}); diff --git a/x-pack/plugins/monitoring/server/lib/alerts/get_prepared_alert.ts b/x-pack/plugins/monitoring/server/lib/alerts/get_prepared_alert.ts new file mode 100644 index 0000000000000..83a9e26e4c589 --- /dev/null +++ b/x-pack/plugins/monitoring/server/lib/alerts/get_prepared_alert.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Logger, ICustomClusterClient, UiSettingsServiceStart } from 'kibana/server'; +import { CallCluster } from 'src/legacy/core_plugins/elasticsearch'; +import { AlertServices } from '../../../../alerting/server'; +import { AlertCommonCluster } from '../../alerts/types'; +import { INDEX_PATTERN_ELASTICSEARCH } from '../../../common/constants'; +import { fetchAvailableCcs } from './fetch_available_ccs'; +import { getCcsIndexPattern } from './get_ccs_index_pattern'; +import { fetchClusters } from './fetch_clusters'; +import { fetchDefaultEmailAddress } from './fetch_default_email_address'; + +export interface PreparedAlert { + emailAddress: string; + clusters: AlertCommonCluster[]; + data: any[]; + timezone: string; + dateFormat: string; +} + +async function getCallCluster( + monitoringCluster: ICustomClusterClient, + services: Pick +): Promise { + if (!monitoringCluster) { + return services.callCluster; + } + + return monitoringCluster.callAsInternalUser; +} + +export async function getPreparedAlert( + alertType: string, + getUiSettingsService: () => Promise, + monitoringCluster: ICustomClusterClient, + logger: Logger, + ccsEnabled: boolean, + services: Pick, + dataFetcher: ( + callCluster: CallCluster, + clusters: AlertCommonCluster[], + esIndexPattern: string + ) => Promise +): Promise { + const callCluster = await getCallCluster(monitoringCluster, services); + + // Support CCS use cases by querying to find available remote clusters + // and then adding those to the index pattern we are searching against + let esIndexPattern = INDEX_PATTERN_ELASTICSEARCH; + if (ccsEnabled) { + const availableCcs = await fetchAvailableCcs(callCluster); + if (availableCcs.length > 0) { + esIndexPattern = getCcsIndexPattern(esIndexPattern, availableCcs); + } + } + + const clusters = await fetchClusters(callCluster, esIndexPattern); + + // Fetch the specific data + const data = await dataFetcher(callCluster, clusters, esIndexPattern); + if (data.length === 0) { + logger.warn(`No data found for ${alertType}.`); + return null; + } + + const uiSettings = (await getUiSettingsService()).asScopedToClient(services.savedObjectsClient); + const dateFormat: string = await uiSettings.get('dateFormat'); + const timezone: string = await uiSettings.get('dateFormat:tz'); + const emailAddress = await fetchDefaultEmailAddress(uiSettings); + if (!emailAddress) { + // TODO: we can do more here + logger.warn(`Unable to send email for ${alertType} because there is no email configured.`); + return null; + } + + return { + emailAddress, + data, + clusters, + dateFormat, + timezone, + }; +} diff --git a/x-pack/plugins/monitoring/server/lib/alerts/license_expiration.lib.test.ts b/x-pack/plugins/monitoring/server/lib/alerts/license_expiration.lib.test.ts index 1a2eb1e44be84..b99208bdde2c8 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/license_expiration.lib.test.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/license_expiration.lib.test.ts @@ -39,17 +39,26 @@ describe('licenseExpiration lib', () => { }); describe('getUiMessage', () => { - const timezone = 'Europe/London'; - const license: any = { expiryDateMS: moment.tz('2020-01-20 08:00:00', timezone).utc() }; - it('should return a message when firing', () => { - const message = getUiMessage(license, timezone, false); - expect(message).toBe(`This cluster's license is going to expire in #relative at #absolute.`); + const message = getUiMessage(false); + expect(message.text).toBe( + `This cluster's license is going to expire in #relative at #absolute. #start_linkPlease update your license.#end_link` + ); + // LOL How do I avoid this in TS???? + if (!message.tokens) { + return expect(false).toBe(true); + } + expect(message.tokens.length).toBe(3); + expect(message.tokens[0].startToken).toBe('#relative'); + expect(message.tokens[1].startToken).toBe('#absolute'); + expect(message.tokens[2].startToken).toBe('#start_link'); + expect(message.tokens[2].endToken).toBe('#end_link'); }); it('should return a message when resolved', () => { - const message = getUiMessage(license, timezone, true); - expect(message).toBe(`This cluster's license is active.`); + const message = getUiMessage(true); + expect(message.text).toBe(`This cluster's license is active.`); + expect(message.tokens).not.toBeDefined(); }); }); }); diff --git a/x-pack/plugins/monitoring/server/lib/alerts/license_expiration.lib.ts b/x-pack/plugins/monitoring/server/lib/alerts/license_expiration.lib.ts index 41b68d69bbd25..cfe9f02b9bd6a 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/license_expiration.lib.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/license_expiration.lib.ts @@ -6,7 +6,13 @@ import { Moment } from 'moment-timezone'; import { i18n } from '@kbn/i18n'; import { AlertInstance } from '../../../../alerting/server'; -import { AlertLicense } from '../../alerts/types'; +import { + AlertCommonPerClusterMessageLinkToken, + AlertCommonPerClusterMessageTimeToken, + AlertCommonCluster, + AlertCommonPerClusterMessage, +} from '../../alerts/types'; +import { AlertCommonPerClusterMessageTokenType } from '../../alerts/enums'; const RESOLVED_SUBJECT = i18n.translate( 'xpack.monitoring.alerts.licenseExpiration.resolvedSubject', @@ -21,7 +27,7 @@ const NEW_SUBJECT = i18n.translate('xpack.monitoring.alerts.licenseExpiration.ne export function executeActions( instance: AlertInstance, - license: AlertLicense, + cluster: AlertCommonCluster, $expiry: Moment, dateFormat: string, emailAddress: string, @@ -31,14 +37,14 @@ export function executeActions( instance.scheduleActions('default', { subject: RESOLVED_SUBJECT, message: `This cluster alert has been resolved: Cluster '${ - license.clusterName + cluster.clusterName }' license was going to expire on ${$expiry.format(dateFormat)}.`, to: emailAddress, }); } else { instance.scheduleActions('default', { subject: NEW_SUBJECT, - message: `Cluster '${license.clusterName}' license is going to expire on ${$expiry.format( + message: `Cluster '${cluster.clusterName}' license is going to expire on ${$expiry.format( dateFormat )}. Please update your license.`, to: emailAddress, @@ -46,13 +52,37 @@ export function executeActions( } } -export function getUiMessage(license: AlertLicense, timezone: string, resolved: boolean = false) { +export function getUiMessage(resolved: boolean = false): AlertCommonPerClusterMessage { if (resolved) { - return i18n.translate('xpack.monitoring.alerts.licenseExpiration.ui.resolvedMessage', { - defaultMessage: `This cluster's license is active.`, - }); + return { + text: i18n.translate('xpack.monitoring.alerts.licenseExpiration.ui.resolvedMessage', { + defaultMessage: `This cluster's license is active.`, + }), + }; } - return i18n.translate('xpack.monitoring.alerts.licenseExpiration.ui.firingMessage', { - defaultMessage: `This cluster's license is going to expire in #relative at #absolute.`, - }); + return { + text: i18n.translate('xpack.monitoring.alerts.licenseExpiration.ui.firingMessage', { + defaultMessage: `This cluster's license is going to expire in #relative at #absolute. #start_linkPlease update your license.#end_link`, + }), + tokens: [ + { + startToken: '#relative', + type: AlertCommonPerClusterMessageTokenType.Time, + isRelative: true, + isAbsolute: false, + } as AlertCommonPerClusterMessageTimeToken, + { + startToken: '#absolute', + type: AlertCommonPerClusterMessageTokenType.Time, + isAbsolute: true, + isRelative: false, + } as AlertCommonPerClusterMessageTimeToken, + { + startToken: '#start_link', + endToken: '#end_link', + type: AlertCommonPerClusterMessageTokenType.Link, + url: 'license', + } as AlertCommonPerClusterMessageLinkToken, + ], + }; } diff --git a/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_from_request.js b/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_from_request.js index c5091c36c3bbe..1bddede52207b 100644 --- a/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_from_request.js +++ b/x-pack/plugins/monitoring/server/lib/cluster/get_clusters_from_request.js @@ -29,6 +29,7 @@ import { CODE_PATH_BEATS, CODE_PATH_APM, KIBANA_ALERTING_ENABLED, + ALERT_TYPES, } from '../../../common/constants'; import { getApmsForClusters } from '../apm/get_apms_for_clusters'; import { i18n } from '@kbn/i18n'; @@ -102,15 +103,8 @@ export async function getClustersFromRequest( if (isInCodePath(codePaths, [CODE_PATH_ALERTS])) { if (KIBANA_ALERTING_ENABLED) { - const { callWithRequest } = req.server.plugins.elasticsearch.getCluster('monitoring'); - const callCluster = (...args) => callWithRequest(req, ...args); - cluster.alerts = await fetchStatus( - callCluster, - start, - end, - cluster.cluster_uuid, - req.server - ); + const alertsClient = req.getAlertsClient ? req.getAlertsClient() : null; + cluster.alerts = await fetchStatus(alertsClient, ALERT_TYPES, start, end, req.logger); } else { cluster.alerts = await alertsClusterSearch( req, diff --git a/x-pack/plugins/monitoring/server/plugin.ts b/x-pack/plugins/monitoring/server/plugin.ts index 24d8bcaa4397c..0fa1a5bf144ac 100644 --- a/x-pack/plugins/monitoring/server/plugin.ts +++ b/x-pack/plugins/monitoring/server/plugin.ts @@ -47,6 +47,7 @@ import { PluginSetupContract as AlertingPluginSetupContract, } from '../../alerting/server'; import { getLicenseExpiration } from './alerts/license_expiration'; +import { getClusterState } from './alerts/cluster_state'; import { InfraPluginSetup } from '../../infra/server'; export interface LegacyAPI { @@ -154,6 +155,17 @@ export class Plugin { config.ui.ccs.enabled ) ); + plugins.alerting.registerType( + getClusterState( + async () => { + const coreStart = (await core.getStartServices())[0]; + return coreStart.uiSettings; + }, + cluster, + this.getLogger, + config.ui.ccs.enabled + ) + ); } // Initialize telemetry @@ -165,12 +177,7 @@ export class Plugin { // Register collector objects for stats to show up in the APIs if (plugins.usageCollection) { - registerCollectors( - plugins.usageCollection, - config, - core.metrics.getOpsMetrics$(), - get(legacyConfig, 'kibana.index') - ); + registerCollectors(plugins.usageCollection, config); } // If collection is enabled, create the bulk uploader @@ -269,18 +276,23 @@ export class Plugin { catalogue: ['monitoring'], privileges: null, reserved: { - privilege: { - app: ['monitoring', 'kibana'], - catalogue: ['monitoring'], - savedObject: { - all: [], - read: [], - }, - ui: [], - }, description: i18n.translate('xpack.monitoring.feature.reserved.description', { defaultMessage: 'To grant users access, you should also assign the monitoring_user role.', }), + privileges: [ + { + id: 'monitoring', + privilege: { + app: ['monitoring', 'kibana'], + catalogue: ['monitoring'], + savedObject: { + all: [], + read: [], + }, + ui: [], + }, + }, + ], }, }); } diff --git a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/alerts.js b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/alerts.js index 56922bd8e87e2..d5a43d32f600a 100644 --- a/x-pack/plugins/monitoring/server/routes/api/v1/alerts/alerts.js +++ b/x-pack/plugins/monitoring/server/routes/api/v1/alerts/alerts.js @@ -8,8 +8,12 @@ import { schema } from '@kbn/config-schema'; import { isFunction } from 'lodash'; import { ALERT_TYPE_LICENSE_EXPIRATION, + ALERT_TYPE_CLUSTER_STATE, MONITORING_CONFIG_ALERTING_EMAIL_ADDRESS, + ALERT_TYPES, } from '../../../../../common/constants'; +import { handleError } from '../../../../lib/errors'; +import { fetchStatus } from '../../../../lib/alerts/fetch_status'; async function createAlerts(req, alertsClient, { selectedEmailActionId }) { const createdAlerts = []; @@ -17,7 +21,21 @@ async function createAlerts(req, alertsClient, { selectedEmailActionId }) { // Create alerts const ALERT_TYPES = { [ALERT_TYPE_LICENSE_EXPIRATION]: { - schedule: { interval: '10s' }, + schedule: { interval: '1m' }, + actions: [ + { + group: 'default', + id: selectedEmailActionId, + params: { + subject: '{{context.subject}}', + message: `{{context.message}}`, + to: ['{{context.to}}'], + }, + }, + ], + }, + [ALERT_TYPE_CLUSTER_STATE]: { + schedule: { interval: '1m' }, actions: [ { group: 'default', @@ -86,4 +104,37 @@ export function createKibanaAlertsRoute(server) { return { alerts, emailResponse }; }, }); + + server.route({ + method: 'POST', + path: '/api/monitoring/v1/alert_status', + config: { + validate: { + payload: schema.object({ + timeRange: schema.object({ + min: schema.string(), + max: schema.string(), + }), + }), + }, + }, + async handler(req, headers) { + const alertsClient = isFunction(req.getAlertsClient) ? req.getAlertsClient() : null; + if (!alertsClient) { + return headers.response().code(404); + } + + const start = req.payload.timeRange.min; + const end = req.payload.timeRange.max; + let alerts; + + try { + alerts = await fetchStatus(alertsClient, ALERT_TYPES, start, end, req.logger); + } catch (err) { + throw handleError(err, req); + } + + return { alerts }; + }, + }); } diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap index 4c109c557fdb0..1e6c2c4d289aa 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/__snapshots__/remote_cluster_form.test.js.snap @@ -350,7 +350,7 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u description={ @@ -367,7 +367,7 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u data-test-subj="remoteClusterFormConnectionModeToggle" label={ @@ -443,11 +443,11 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u className="euiTextColor euiTextColor--subdued" > - Use seed nodes by default, or switch to a single proxy address. + Use seed nodes by default, or switch to proxy mode. @@ -534,11 +534,11 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u onClick={[Function]} > - Use a single proxy address + Use proxy mode @@ -687,23 +687,37 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u + + , + } + } /> } + isInvalid={false} label={ } @@ -711,28 +725,31 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u >
@@ -740,20 +757,19 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u
-
- + - +
- + + + , + } + } > - The number of socket connections to open per remote cluster. + A string sent in the server_name field of the TLS Server Name Indication extension if TLS is enabled. + + +
@@ -801,37 +845,23 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u
- - , - } - } + defaultMessage="The number of socket connections to open per remote cluster." + id="xpack.remoteClusters.remoteClusterForm.fieldSocketConnectionsHelpText" + values={Object {}} /> } - isInvalid={false} label={ } @@ -839,31 +869,28 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u >
@@ -871,19 +898,20 @@ exports[`RemoteClusterForm proxy mode renders correct connection settings when u
-
- + - +
- + - - , - } - } + defaultMessage="The number of socket connections to open per remote cluster." + id="xpack.remoteClusters.remoteClusterForm.fieldSocketConnectionsHelpText" + values={Object {}} > - A string sent in the server_name field of the TLS Server Name Indication extension if TLS is enabled. - - - + The number of socket connections to open per remote cluster.
@@ -1447,7 +1447,7 @@ Array [
- Use seed nodes by default, or switch to a single proxy address. + Use seed nodes by default, or switch to proxy mode.
- Use a single proxy address + Use proxy mode
diff --git a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js index c98bd73bf83a0..a392cc9607784 100644 --- a/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js +++ b/x-pack/plugins/remote_clusters/public/application/sections/components/remote_cluster_form/remote_cluster_form.js @@ -405,30 +405,6 @@ export class RemoteClusterForm extends Component { /> - - } - helpText={ - - } - fullWidth - > - - this.onFieldsChange({ proxySocketConnections: Number(e.target.value) || null }) - } - fullWidth - /> - + + + } + helpText={ + + } + fullWidth + > + + this.onFieldsChange({ proxySocketConnections: Number(e.target.value) || null }) + } + fullWidth + /> + ); } @@ -498,14 +499,14 @@ export class RemoteClusterForm extends Component { <> } checked={mode === PROXY_MODE} @@ -519,15 +520,38 @@ export class RemoteClusterForm extends Component { <> } - iconType="pin" - size="s" - /> + > + + + + ), + searchString: ( + + + + ), + }} + /> + ) : null} diff --git a/x-pack/plugins/reporting/public/components/__snapshots__/report_listing.test.tsx.snap b/x-pack/plugins/reporting/public/components/__snapshots__/report_listing.test.tsx.snap index 75bc1f9eea696..271d61c224210 100644 --- a/x-pack/plugins/reporting/public/components/__snapshots__/report_listing.test.tsx.snap +++ b/x-pack/plugins/reporting/public/components/__snapshots__/report_listing.test.tsx.snap @@ -2,525 +2,6 @@ exports[`ReportListing Report job listing with some items 1`] = ` Array [ - -
- - -
- -
- - - -
-
- - - - -
- - -
-
- - - -
- -
- - - -
- - -
-
- -
- -
- -
- - -
- -
- -
- - -
- - -
- -
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
- - -
- -
-
- - -
-
-
- - Report - -
-
-
- - Created at - -
-
-
- - Status - -
-
-
- - Actions - -
-
-
- - Loading reports - -
-
-
-
-
- -
- , { throw error; } } + + // Since the contents of the table have changed, we must reset the pagination + // and re-fetch. Otherwise, the Nth page we are on could be empty of jobs. + this.setState(() => ({ page: 0 }), this.fetchJobs); }; return ( @@ -476,34 +480,32 @@ class ReportListingUi extends Component { onSelectionChange: this.onSelectionChange, }; - const search = { - toolsRight: this.renderDeleteButton(), - }; - return ( - + + + {this.state.selectedJobs.length > 0 ? this.renderDeleteButton() : null} + ); } } diff --git a/x-pack/plugins/security/server/authorization/index.ts b/x-pack/plugins/security/server/authorization/index.ts index f065c9cfd90ba..cf970a561b93f 100644 --- a/x-pack/plugins/security/server/authorization/index.ts +++ b/x-pack/plugins/security/server/authorization/index.ts @@ -29,6 +29,7 @@ import { initAppAuthorization } from './app_authorization'; import { initAPIAuthorization } from './api_authorization'; import { disableUICapabilitiesFactory } from './disable_ui_capabilities'; import { validateFeaturePrivileges } from './validate_feature_privileges'; +import { validateReservedPrivileges } from './validate_reserved_privileges'; import { registerPrivilegesWithCluster } from './register_privileges_with_cluster'; import { APPLICATION_PREFIX } from '../../common/constants'; import { SecurityLicense } from '../../common/licensing'; @@ -121,7 +122,9 @@ export function setupAuthorization({ }, registerPrivilegesWithCluster: async () => { - validateFeaturePrivileges(featuresService.getFeatures()); + const features = featuresService.getFeatures(); + validateFeaturePrivileges(features); + validateReservedPrivileges(features); await registerPrivilegesWithCluster(logger, privileges, applicationName, clusterClient); }, diff --git a/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts b/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts index 3d25fc03f568b..b023c12d35b79 100644 --- a/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts +++ b/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts @@ -409,13 +409,18 @@ describe('features', () => { }, privileges: null, reserved: { - privilege: { - savedObject: { - all: ['ignore-me-1', 'ignore-me-2'], - read: ['ignore-me-1', 'ignore-me-2'], + privileges: [ + { + id: 'reserved', + privilege: { + savedObject: { + all: ['ignore-me-1', 'ignore-me-2'], + read: ['ignore-me-1', 'ignore-me-2'], + }, + ui: ['ignore-me-1'], + }, }, - ui: ['ignore-me-1'], - }, + ], description: '', }, }), @@ -591,13 +596,18 @@ describe('reserved', () => { }, privileges: null, reserved: { - privilege: { - savedObject: { - all: [], - read: [], + privileges: [ + { + id: 'foo', + privilege: { + savedObject: { + all: [], + read: [], + }, + ui: [], + }, }, - ui: [], - }, + ], description: '', }, }), @@ -627,13 +637,18 @@ describe('reserved', () => { app: [], privileges: null, reserved: { - privilege: { - savedObject: { - all: ['savedObject-all-1', 'savedObject-all-2'], - read: ['savedObject-read-1', 'savedObject-read-2'], + privileges: [ + { + id: 'foo', + privilege: { + savedObject: { + all: ['savedObject-all-1', 'savedObject-all-2'], + read: ['savedObject-read-1', 'savedObject-read-2'], + }, + ui: ['ui-1', 'ui-2'], + }, }, - ui: ['ui-1', 'ui-2'], - }, + ], description: '', }, }), diff --git a/x-pack/plugins/security/server/authorization/privileges/privileges.ts b/x-pack/plugins/security/server/authorization/privileges/privileges.ts index b25aad30a3423..9a8935f80a174 100644 --- a/x-pack/plugins/security/server/authorization/privileges/privileges.ts +++ b/x-pack/plugins/security/server/authorization/privileges/privileges.ts @@ -110,10 +110,12 @@ export function privilegesFactory( }, reserved: features.reduce((acc: Record, feature: Feature) => { if (feature.reserved) { - acc[feature.id] = [ - actions.version, - ...featurePrivilegeBuilder.getActions(feature.reserved!.privilege, feature), - ]; + feature.reserved.privileges.forEach(reservedPrivilege => { + acc[reservedPrivilege.id] = [ + actions.version, + ...uniq(featurePrivilegeBuilder.getActions(reservedPrivilege.privilege, feature)), + ]; + }); } return acc; }, {}), diff --git a/x-pack/plugins/security/server/authorization/validate_feature_privileges.test.ts b/x-pack/plugins/security/server/authorization/validate_feature_privileges.test.ts index ac386d287cff1..cd2c7faa263c9 100644 --- a/x-pack/plugins/security/server/authorization/validate_feature_privileges.test.ts +++ b/x-pack/plugins/security/server/authorization/validate_feature_privileges.test.ts @@ -26,13 +26,18 @@ it('allows features with reserved privileges to be defined', () => { privileges: null, reserved: { description: 'foo', - privilege: { - savedObject: { - all: ['foo'], - read: ['bar'], + privileges: [ + { + id: 'reserved', + privilege: { + savedObject: { + all: ['foo'], + read: ['bar'], + }, + ui: [], + }, }, - ui: [], - }, + ], }, }); diff --git a/x-pack/plugins/security/server/authorization/validate_reserved_privileges.test.ts b/x-pack/plugins/security/server/authorization/validate_reserved_privileges.test.ts new file mode 100644 index 0000000000000..26af0dadfb288 --- /dev/null +++ b/x-pack/plugins/security/server/authorization/validate_reserved_privileges.test.ts @@ -0,0 +1,181 @@ +/* + * 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 { Feature } from '../../../features/server'; +import { validateReservedPrivileges } from './validate_reserved_privileges'; + +it('allows features to be defined without privileges', () => { + const feature: Feature = new Feature({ + id: 'foo', + name: 'foo', + app: [], + privileges: null, + }); + + validateReservedPrivileges([feature]); +}); + +it('allows features with a single reserved privilege to be defined', () => { + const feature: Feature = new Feature({ + id: 'foo', + name: 'foo', + app: [], + privileges: null, + reserved: { + description: 'foo', + privileges: [ + { + id: 'reserved', + privilege: { + savedObject: { + all: ['foo'], + read: ['bar'], + }, + ui: [], + }, + }, + ], + }, + }); + + validateReservedPrivileges([feature]); +}); + +it('allows multiple features with reserved privileges to be defined', () => { + const feature1: Feature = new Feature({ + id: 'foo', + name: 'foo', + app: [], + privileges: null, + reserved: { + description: 'foo', + privileges: [ + { + id: 'reserved-1', + privilege: { + savedObject: { + all: ['foo'], + read: ['bar'], + }, + ui: [], + }, + }, + ], + }, + }); + + const feature2: Feature = new Feature({ + id: 'foo2', + name: 'foo', + app: [], + privileges: null, + reserved: { + description: 'foo', + privileges: [ + { + id: 'reserved-2', + privilege: { + savedObject: { + all: ['foo'], + read: ['bar'], + }, + ui: [], + }, + }, + ], + }, + }); + + validateReservedPrivileges([feature1, feature2]); +}); + +it('prevents a feature from specifying the same reserved privilege id', () => { + const feature1: Feature = new Feature({ + id: 'foo', + name: 'foo', + app: [], + privileges: null, + reserved: { + description: 'foo', + privileges: [ + { + id: 'reserved', + privilege: { + savedObject: { + all: ['foo'], + read: ['bar'], + }, + ui: [], + }, + }, + { + id: 'reserved', + privilege: { + savedObject: { + all: ['foo'], + read: ['bar'], + }, + ui: [], + }, + }, + ], + }, + }); + + expect(() => validateReservedPrivileges([feature1])).toThrowErrorMatchingInlineSnapshot( + `"Duplicate reserved privilege id detected: reserved. This is not allowed."` + ); +}); + +it('prevents features from sharing a reserved privilege id', () => { + const feature1: Feature = new Feature({ + id: 'foo', + name: 'foo', + app: [], + privileges: null, + reserved: { + description: 'foo', + privileges: [ + { + id: 'reserved', + privilege: { + savedObject: { + all: ['foo'], + read: ['bar'], + }, + ui: [], + }, + }, + ], + }, + }); + + const feature2: Feature = new Feature({ + id: 'foo2', + name: 'foo', + app: [], + privileges: null, + reserved: { + description: 'foo', + privileges: [ + { + id: 'reserved', + privilege: { + savedObject: { + all: ['foo'], + read: ['bar'], + }, + ui: [], + }, + }, + ], + }, + }); + + expect(() => validateReservedPrivileges([feature1, feature2])).toThrowErrorMatchingInlineSnapshot( + `"Duplicate reserved privilege id detected: reserved. This is not allowed."` + ); +}); diff --git a/x-pack/plugins/security/server/authorization/validate_reserved_privileges.ts b/x-pack/plugins/security/server/authorization/validate_reserved_privileges.ts new file mode 100644 index 0000000000000..0915308fc0f89 --- /dev/null +++ b/x-pack/plugins/security/server/authorization/validate_reserved_privileges.ts @@ -0,0 +1,20 @@ +/* + * 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 { Feature } from '../../../features/server'; + +export function validateReservedPrivileges(features: Feature[]) { + const seenPrivilegeIds = new Set(); + + for (const feature of features) { + (feature?.reserved?.privileges ?? []).forEach(({ id }) => { + if (seenPrivilegeIds.has(id)) { + throw new Error(`Duplicate reserved privilege id detected: ${id}. This is not allowed.`); + } + seenPrivilegeIds.add(id); + }); + } +} diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.test.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.test.tsx index 7809b511adda4..99b4e184c071a 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.test.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.test.tsx @@ -6,7 +6,6 @@ import React from 'react'; import Boom from 'boom'; import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; -import { mockManagementPlugin } from '../../../../../../src/legacy/core_plugins/management/public/np_ready/mocks'; import { CopySavedObjectsToSpaceFlyout } from './copy_to_space_flyout'; import { CopyToSpaceForm } from './copy_to_space_form'; import { EuiLoadingSpinner, EuiEmptyPrompt } from '@elastic/eui'; @@ -19,11 +18,6 @@ import { spacesManagerMock } from '../../spaces_manager/mocks'; import { SpacesManager } from '../../spaces_manager'; import { ToastsApi } from 'src/core/public'; -jest.mock('../../../../../../src/legacy/core_plugins/management/public/legacy', () => ({ - setup: mockManagementPlugin.createSetupContract(), - start: mockManagementPlugin.createStartContract(), -})); - interface SetupOpts { mockSpaces?: Space[]; returnBeforeSpacesLoad?: boolean; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.tsx index 4d92505c4aebb..fee41fc7e36d3 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout.tsx @@ -25,7 +25,7 @@ import { ToastsStart } from 'src/core/public'; import { ProcessedImportResponse, processImportResponse, -} from '../../../../../../src/legacy/core_plugins/management/public'; +} from '../../../../../../src/legacy/core_plugins/kibana/public'; import { SavedObjectsManagementRecord } from '../../../../../../src/plugins/saved_objects_management/public'; import { Space } from '../../../common/model/space'; import { SpacesManager } from '../../spaces_manager'; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_footer.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_footer.tsx index b22cec0af5ea8..4f6ff55dbfbb2 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_footer.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/copy_to_space_flyout_footer.tsx @@ -8,7 +8,7 @@ import React, { Fragment } from 'react'; import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiStat, EuiHorizontalRule } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { ProcessedImportResponse } from '../../../../../../src/legacy/core_plugins/management/public'; +import { ProcessedImportResponse } from '../../../../../../src/legacy/core_plugins/kibana/public'; import { ImportRetry } from '../types'; interface Props { diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/processing_copy_to_space.tsx b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/processing_copy_to_space.tsx index 96cbac4b48065..ea74fc92b95ea 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/processing_copy_to_space.tsx +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/components/processing_copy_to_space.tsx @@ -13,7 +13,7 @@ import { EuiHorizontalRule, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ProcessedImportResponse } from '../../../../../../src/legacy/core_plugins/management/public'; +import { ProcessedImportResponse } from '../../../../../../src/legacy/core_plugins/kibana/public'; import { SavedObjectsManagementRecord } from '../../../../../../src/plugins/saved_objects_management/public'; import { Space } from '../../../common/model/space'; import { CopyOptions, ImportRetry } from '../types'; diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.test.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.test.ts index fb2616619c644..65a0cabfeb716 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.test.ts +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.test.ts @@ -5,7 +5,7 @@ */ import { summarizeCopyResult } from './summarize_copy_result'; -import { ProcessedImportResponse } from 'src/legacy/core_plugins/management/public'; +import { ProcessedImportResponse } from 'src/legacy/core_plugins/kibana/public'; const createSavedObjectsManagementRecord = () => ({ type: 'dashboard', diff --git a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.ts b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.ts index 96e642b0f45d8..44c9e9993bf10 100644 --- a/x-pack/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.ts +++ b/x-pack/plugins/spaces/public/copy_saved_objects_to_space/summarize_copy_result.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ProcessedImportResponse } from 'src/legacy/core_plugins/management/public'; +import { ProcessedImportResponse } from 'src/legacy/core_plugins/kibana/public'; import { SavedObjectsManagementRecord } from 'src/plugins/saved_objects_management/public'; export interface SummarizedSavedObjectResult { diff --git a/x-pack/plugins/transform/public/app/hooks/use_search_items/use_search_items.ts b/x-pack/plugins/transform/public/app/hooks/use_search_items/use_search_items.ts index f5f9e98fe659c..feff17b813112 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_search_items/use_search_items.ts +++ b/x-pack/plugins/transform/public/app/hooks/use_search_items/use_search_items.ts @@ -29,6 +29,7 @@ export const useSearchItems = (defaultSavedObjectId: string | undefined) => { const savedSearches = createSavedSearchesLoader({ savedObjectsClient, indexPatterns, + search: appDeps.data.search, chrome: appDeps.chrome, overlays: appDeps.overlays, }); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 2ae29202ede43..e63e1c8ad2c91 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -2523,12 +2523,10 @@ "management.breadcrumb": "管理", "management.connectDataDisplayName": "データに接続", "management.displayName": "管理", - "management.editIndexPattern.createIndex.defaultButtonDescription": "すべてのデータに完全集約を実行", - "management.editIndexPattern.createIndex.defaultButtonText": "標準インデックスパターン", - "management.editIndexPattern.createIndex.defaultTypeName": "インデックスパターン", - "management.editIndexPattern.list.defaultIndexPatternListName": "デフォルト", - "management.indexPatternHeader": "インデックスパターン", - "management.indexPatternLabel": "Elasticsearch からのデータの取得に役立つインデックスパターンを管理します。", + "indexPatternManagement.editIndexPattern.createIndex.defaultButtonDescription": "すべてのデータに完全集約を実行", + "indexPatternManagement.editIndexPattern.createIndex.defaultButtonText": "標準インデックスパターン", + "indexPatternManagement.editIndexPattern.createIndex.defaultTypeName": "インデックスパターン", + "indexPatternManagement.editIndexPattern.list.defaultIndexPatternListName": "デフォルト", "management.nav.label": "管理", "management.nav.menu": "管理メニュー", "management.stackManagement.managementDescription": "Elastic Stack の管理を行うセンターコンソールです。", @@ -9732,7 +9730,6 @@ "xpack.ml.datavisualizer.selector.dataVisualizerTitle": "データビジュアライザー", "xpack.ml.datavisualizer.selector.experimentalBadgeLabel": "実験的", "xpack.ml.datavisualizer.selector.experimentalBadgeTooltipLabel": "実験的機能。フィードバックをお待ちしています。", - "xpack.ml.datavisualizer.selector.importDataDescription": "ログファイルからデータをインポートします。最大 100 MB のファイルをアップロードできます。", "xpack.ml.datavisualizer.selector.importDataTitle": "データのインポート", "xpack.ml.datavisualizer.selector.selectIndexButtonLabel": "インデックスを選択", "xpack.ml.datavisualizer.selector.selectIndexPatternDescription": "既存の Elasticsearch インデックスのデータを可視化します。", @@ -9983,7 +9980,6 @@ "xpack.ml.fileDatavisualizer.welcomeContent.logFilesWithCommonFormatDescription": "タイムスタンプの一般的フォーマットのログファイル", "xpack.ml.fileDatavisualizer.welcomeContent.newlineDelimitedJsonDescription": "改行区切りの JSON", "xpack.ml.fileDatavisualizer.welcomeContent.supportedFileFormatDescription": "ファイルデータビジュアライザーはこれらのファイル形式をサポートしています:", - "xpack.ml.fileDatavisualizer.welcomeContent.uploadedFilesAllowedSizeDescription": "最大 100 MB のファイルをアップロードできます。", "xpack.ml.fileDatavisualizer.welcomeContent.visualizeDataFromLogFileDescription": "ファイルデータビジュアライザーは、ログファイルのフィールドとメトリックの理解に役立ちます。ファイルをアップロードして、データを分析し、 Elasticsearch インデックスにインポートするか選択できます。", "xpack.ml.fileDatavisualizer.welcomeContent.visualizeDataFromLogFileTitle": "ログファイルのデータを可視化 {experimentalBadge}", "xpack.ml.formatters.metricChangeDescription.actualSameAsTypicalDescription": "実際値が通常値と同じ", @@ -15868,8 +15864,6 @@ "xpack.triggersActionsUI.common.expressionItems.threshold.andLabel": "AND", "xpack.triggersActionsUI.common.expressionItems.threshold.descriptionLabel": "タイミング", "xpack.triggersActionsUI.common.expressionItems.threshold.popoverTitle": "タイミング", - "xpack.triggersActionsUI.components.alertActionSecurityCallOut.enableTlsCta": "TLS を有効にする", - "xpack.triggersActionsUI.components.alertActionSecurityCallOut.tlsDisabledTitle": "アラート {action} を実行するには Elasticsearch と Kibana の間に TLS が必要です。", "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.actionTypeTitle": "メールに送信", "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.addVariablePopoverButton": "変数を追加", "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.selectMessageText": "サーバーからメールを送信します。", @@ -15964,9 +15958,6 @@ "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.viewHeadersSwitch": "HTTP ヘッダーを追加", "xpack.triggersActionsUI.components.deleteSelectedIdsErrorNotification.descriptionText": "{numErrors, number} {numErrors, plural, one {{singleTitle}} other {{multipleTitle}}}を削除できませんでした", "xpack.triggersActionsUI.components.deleteSelectedIdsSuccessNotification.descriptionText": "{numSuccesses, number} {numSuccesses, plural, one {{singleTitle}} other {{multipleTitle}}}を削除しました", - "xpack.triggersActionsUI.components.securityCallOut.enableTlsCta": "TLS を有効にする", - "xpack.triggersActionsUI.components.securityCallOut.tlsDisabledDescription": "アラートは API キー に依存し、キーを使用するには Elasticsearch と Kibana の間に TLS が必要です。", - "xpack.triggersActionsUI.components.securityCallOut.tlsDisabledTitle": "トランスポートレイヤーセキュリティを有効にする", "xpack.triggersActionsUI.connectors.breadcrumbTitle": "コネクター", "xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.cancelButtonLabel": "キャンセル", "xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.deleteButtonLabel": "{numIdsToDelete, plural, one {{singleTitle}} other {# {multipleTitle}}}を削除 ", @@ -15990,8 +15981,8 @@ "xpack.triggersActionsUI.sections.actionConnectorForm.error.requiredNameText": "名前が必要です。", "xpack.triggersActionsUI.sections.actionForm.getMoreActionsTitle": "さらにアクションを表示", "xpack.triggersActionsUI.sections.actionsConnectorsList.addActionButtonLabel": "コネクターを作成", - "xpack.triggersActionsUI.sections.actionsConnectorsList.addActionEmptyBody": "Kibana でトリガーできるメール、Slack, Elasticsearch、およびサードパーティサービスを構成します。", - "xpack.triggersActionsUI.sections.actionsConnectorsList.addActionEmptyTitle": "初めてのコネクターを作成する", + "xpack.triggersActionsUI.components.emptyConnectorsPrompt.addActionEmptyBody": "Kibana でトリガーできるメール、Slack, Elasticsearch、およびサードパーティサービスを構成します。", + "xpack.triggersActionsUI.components.emptyConnectorsPrompt.addActionEmptyTitle": "初めてのコネクターを作成する", "xpack.triggersActionsUI.sections.actionsConnectorsList.buttons.deleteDisabledTitle": "コネクターを削除できません", "xpack.triggersActionsUI.sections.actionsConnectorsList.buttons.deleteLabel": "{count} 件を削除", "xpack.triggersActionsUI.sections.actionsConnectorsList.connectorsListTable.columns.actions.deleteActionDescription": "このコネクターを削除", @@ -16041,7 +16032,6 @@ "xpack.triggersActionsUI.sections.alertAdd.saveButtonLabel": "保存", "xpack.triggersActionsUI.sections.alertAdd.saveErrorNotificationText": "アラートを作成できません。", "xpack.triggersActionsUI.sections.alertAdd.saveSuccessNotificationText": "「{alertName}」 を保存しました", - "xpack.triggersActionsUI.sections.alertAdd.securityCalloutAction": "作成", "xpack.triggersActionsUI.sections.alertAdd.selectIndex": "インデックスを選択してください。", "xpack.triggersActionsUI.sections.alertAdd.threshold.closeIndexPopoverLabel": "閉じる", "xpack.triggersActionsUI.sections.alertAdd.threshold.fixErrorInExpressionBelowValidationMessage": "下の表現のエラーを修正してください。", @@ -16078,7 +16068,6 @@ "xpack.triggersActionsUI.sections.alertEdit.saveButtonLabel": "保存", "xpack.triggersActionsUI.sections.alertEdit.saveErrorNotificationText": "アラートを更新できません。", "xpack.triggersActionsUI.sections.alertEdit.saveSuccessNotificationText": "「{alertName}」 を更新しました", - "xpack.triggersActionsUI.sections.alertEdit.securityCalloutAction": "編集中", "xpack.triggersActionsUI.sections.alertForm.accordion.deleteIconAriaLabel": "削除", "xpack.triggersActionsUI.sections.alertForm.actionDisabledTitle": "このアクションは無効です", "xpack.triggersActionsUI.sections.alertForm.actionIdLabel": "{connectorInstance} コネクター", @@ -16130,9 +16119,9 @@ "xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.enableTitle": "有効にする", "xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.muteTitle": "ミュート", "xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.popoverButtonTitle": "アクション", - "xpack.triggersActionsUI.sections.alertsList.emptyButton": "アラートの作成", - "xpack.triggersActionsUI.sections.alertsList.emptyDesc": "トリガーが起きたときにメール、Slack、または別のコネクターを通してアラートを受信します。", - "xpack.triggersActionsUI.sections.alertsList.emptyTitle": "初めてのアラートを作成する", + "xpack.triggersActionsUI.components.emptyPrompt.emptyButton": "アラートの作成", + "xpack.triggersActionsUI.components.emptyPrompt.emptyDesc": "トリガーが起きたときにメール、Slack、または別のコネクターを通してアラートを受信します。", + "xpack.triggersActionsUI.components.emptyPrompt.emptyTitle": "初めてのアラートを作成する", "xpack.triggersActionsUI.sections.alertsList.multipleTitle": "アラート", "xpack.triggersActionsUI.sections.alertsList.searchPlaceholderTitle": "検索", "xpack.triggersActionsUI.sections.alertsList.singleTitle": "アラート", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 7f5df15dec83a..cc75ceb988d97 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -2524,12 +2524,10 @@ "management.breadcrumb": "管理", "management.connectDataDisplayName": "连接数据", "management.displayName": "管理", - "management.editIndexPattern.createIndex.defaultButtonDescription": "对任何数据执行完全聚合", - "management.editIndexPattern.createIndex.defaultButtonText": "标准索引模式", - "management.editIndexPattern.createIndex.defaultTypeName": "索引模式", - "management.editIndexPattern.list.defaultIndexPatternListName": "默认值", - "management.indexPatternHeader": "索引模式", - "management.indexPatternLabel": "管理帮助从 Elasticsearch 检索数据的索引模式。", + "indexPatternManagement.editIndexPattern.createIndex.defaultButtonDescription": "对任何数据执行完全聚合", + "indexPatternManagement.editIndexPattern.createIndex.defaultButtonText": "标准索引模式", + "indexPatternManagement.editIndexPattern.createIndex.defaultTypeName": "索引模式", + "indexPatternManagement.editIndexPattern.list.defaultIndexPatternListName": "默认值", "management.nav.label": "管理", "management.nav.menu": "管理菜单", "management.stackManagement.managementDescription": "您用于管理 Elastic Stack 的中心控制台。", @@ -9735,7 +9733,6 @@ "xpack.ml.datavisualizer.selector.dataVisualizerTitle": "数据可视化工具", "xpack.ml.datavisualizer.selector.experimentalBadgeLabel": "实验性", "xpack.ml.datavisualizer.selector.experimentalBadgeTooltipLabel": "实验功能。我们很乐意听取您的反馈意见。", - "xpack.ml.datavisualizer.selector.importDataDescription": "从日志文件导入数据。您可以上传最大 100 MB 的文件。", "xpack.ml.datavisualizer.selector.importDataTitle": "导入数据", "xpack.ml.datavisualizer.selector.selectIndexButtonLabel": "选择索引", "xpack.ml.datavisualizer.selector.selectIndexPatternDescription": "可视化现有 Elasticsearch 索引中的数据。", @@ -9986,7 +9983,6 @@ "xpack.ml.fileDatavisualizer.welcomeContent.logFilesWithCommonFormatDescription": "具有时间戳通用格式的日志文件", "xpack.ml.fileDatavisualizer.welcomeContent.newlineDelimitedJsonDescription": "换行符分隔的 JSON", "xpack.ml.fileDatavisualizer.welcomeContent.supportedFileFormatDescription": "File Data Visualizer 支持以下文件格式:", - "xpack.ml.fileDatavisualizer.welcomeContent.uploadedFilesAllowedSizeDescription": "您可以上传最大 100 MB 的文件。", "xpack.ml.fileDatavisualizer.welcomeContent.visualizeDataFromLogFileDescription": "File Data Visualizer 可帮助您理解日志文件中的字段和指标。上传文件、分析文件数据,然后选择是否将数据导入 Elasticsearch 索引。", "xpack.ml.fileDatavisualizer.welcomeContent.visualizeDataFromLogFileTitle": "可视化来自日志文件的数据 {experimentalBadge}", "xpack.ml.formatters.metricChangeDescription.actualSameAsTypicalDescription": "实际上与典型模式相同", @@ -15872,8 +15868,6 @@ "xpack.triggersActionsUI.common.expressionItems.threshold.andLabel": "且", "xpack.triggersActionsUI.common.expressionItems.threshold.descriptionLabel": "当", "xpack.triggersActionsUI.common.expressionItems.threshold.popoverTitle": "当", - "xpack.triggersActionsUI.components.alertActionSecurityCallOut.enableTlsCta": "启用 TLS", - "xpack.triggersActionsUI.components.alertActionSecurityCallOut.tlsDisabledTitle": "告警 {action} 在 Elasticsearch 和 Kibana 之间需要 TLS。", "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.actionTypeTitle": "发送到电子邮件", "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.addVariablePopoverButton": "添加变量", "xpack.triggersActionsUI.components.builtinActionTypes.emailAction.selectMessageText": "从您的服务器发送电子邮件。", @@ -15968,9 +15962,6 @@ "xpack.triggersActionsUI.components.builtinActionTypes.webhookAction.viewHeadersSwitch": "添加 HTTP 标头", "xpack.triggersActionsUI.components.deleteSelectedIdsErrorNotification.descriptionText": "无法删除 {numErrors, number} 个{numErrors, plural, one {{singleTitle}} other {{multipleTitle}}}", "xpack.triggersActionsUI.components.deleteSelectedIdsSuccessNotification.descriptionText": "已删除 {numSuccesses, number} 个{numSuccesses, plural, one {{singleTitle}} other {{multipleTitle}}}", - "xpack.triggersActionsUI.components.securityCallOut.enableTlsCta": "启用 TLS", - "xpack.triggersActionsUI.components.securityCallOut.tlsDisabledDescription": "Alerting 依赖于在 Elasticsearch 和 Kibana 之间需要 TLS 的 API 密钥。", - "xpack.triggersActionsUI.components.securityCallOut.tlsDisabledTitle": "启用传输层安全", "xpack.triggersActionsUI.connectors.breadcrumbTitle": "连接器", "xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.cancelButtonLabel": "取消", "xpack.triggersActionsUI.deleteSelectedIdsConfirmModal.deleteButtonLabel": "删除{numIdsToDelete, plural, one {{singleTitle}} other { # 个{multipleTitle}}} ", @@ -15995,8 +15986,8 @@ "xpack.triggersActionsUI.sections.actionConnectorForm.error.requiredNameText": "名称必填。", "xpack.triggersActionsUI.sections.actionForm.getMoreActionsTitle": "获取更多的操作", "xpack.triggersActionsUI.sections.actionsConnectorsList.addActionButtonLabel": "创建连接器", - "xpack.triggersActionsUI.sections.actionsConnectorsList.addActionEmptyBody": "配置电子邮件、Slack、Elasticsearch 和 Kibana 可以触发的第三方服务。", - "xpack.triggersActionsUI.sections.actionsConnectorsList.addActionEmptyTitle": "创建您的首个连接器", + "xpack.triggersActionsUI.components.emptyConnectorsPrompt.addActionEmptyBody": "配置电子邮件、Slack、Elasticsearch 和 Kibana 可以触发的第三方服务。", + "xpack.triggersActionsUI.components.emptyConnectorsPrompt.addActionEmptyTitle": "创建您的首个连接器", "xpack.triggersActionsUI.sections.actionsConnectorsList.buttons.deleteDisabledTitle": "无法删除连接器", "xpack.triggersActionsUI.sections.actionsConnectorsList.buttons.deleteLabel": "删除 {count} 个", "xpack.triggersActionsUI.sections.actionsConnectorsList.connectorsListTable.columns.actions.deleteActionDescription": "删除此连接器", @@ -16046,7 +16037,6 @@ "xpack.triggersActionsUI.sections.alertAdd.saveButtonLabel": "保存", "xpack.triggersActionsUI.sections.alertAdd.saveErrorNotificationText": "无法创建告警。", "xpack.triggersActionsUI.sections.alertAdd.saveSuccessNotificationText": "已保存“{alertName}”", - "xpack.triggersActionsUI.sections.alertAdd.securityCalloutAction": "创建", "xpack.triggersActionsUI.sections.alertAdd.selectIndex": "选择索引。", "xpack.triggersActionsUI.sections.alertAdd.threshold.closeIndexPopoverLabel": "关闭", "xpack.triggersActionsUI.sections.alertAdd.threshold.fixErrorInExpressionBelowValidationMessage": "表达式包含错误。", @@ -16083,7 +16073,6 @@ "xpack.triggersActionsUI.sections.alertEdit.saveButtonLabel": "保存", "xpack.triggersActionsUI.sections.alertEdit.saveErrorNotificationText": "无法更新告警。", "xpack.triggersActionsUI.sections.alertEdit.saveSuccessNotificationText": "已更新“{alertName}”", - "xpack.triggersActionsUI.sections.alertEdit.securityCalloutAction": "正在编辑", "xpack.triggersActionsUI.sections.alertForm.accordion.deleteIconAriaLabel": "删除", "xpack.triggersActionsUI.sections.alertForm.actionDisabledTitle": "此操作已禁用", "xpack.triggersActionsUI.sections.alertForm.actionIdLabel": "{connectorInstance} 连接器", @@ -16135,9 +16124,9 @@ "xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.enableTitle": "启用", "xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.muteTitle": "静音", "xpack.triggersActionsUI.sections.alertsList.collapsedItemActons.popoverButtonTitle": "操作", - "xpack.triggersActionsUI.sections.alertsList.emptyButton": "创建告警", - "xpack.triggersActionsUI.sections.alertsList.emptyDesc": "触发条件满足时通过电子邮件、Slack 或其他连接器接收告警。", - "xpack.triggersActionsUI.sections.alertsList.emptyTitle": "创建您的首个告警", + "xpack.triggersActionsUI.components.emptyPrompt.emptyButton": "创建告警", + "xpack.triggersActionsUI.components.emptyPrompt.emptyDesc": "触发条件满足时通过电子邮件、Slack 或其他连接器接收告警。", + "xpack.triggersActionsUI.components.emptyPrompt.emptyTitle": "创建您的首个告警", "xpack.triggersActionsUI.sections.alertsList.multipleTitle": "告警", "xpack.triggersActionsUI.sections.alertsList.searchPlaceholderTitle": "搜索", "xpack.triggersActionsUI.sections.alertsList.singleTitle": "告警", diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/alert_action_security_call_out.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/alert_action_security_call_out.test.tsx deleted file mode 100644 index 85699cfbd750f..0000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/alert_action_security_call_out.test.tsx +++ /dev/null @@ -1,78 +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, { Fragment } from 'react'; -import { shallow, ShallowWrapper } from 'enzyme'; -import { AlertActionSecurityCallOut } from './alert_action_security_call_out'; - -import { EuiCallOut, EuiButton } from '@elastic/eui'; -import { act } from 'react-dom/test-utils'; -import { httpServiceMock } from '../../../../../../src/core/public/mocks'; - -const docLinks = { ELASTIC_WEBSITE_URL: 'elastic.co/', DOC_LINK_VERSION: 'current' }; - -const http = httpServiceMock.createStartContract(); - -describe('alert action security call out', () => { - let useEffect: any; - - const mockUseEffect = () => { - // make react execute useEffects despite shallow rendering - useEffect.mockImplementationOnce((f: Function) => f()); - }; - - beforeEach(() => { - jest.resetAllMocks(); - useEffect = jest.spyOn(React, 'useEffect'); - mockUseEffect(); - }); - - test('renders nothing while health is loading', async () => { - http.get.mockImplementationOnce(() => new Promise(() => {})); - - let component: ShallowWrapper | undefined; - await act(async () => { - component = shallow( - - ); - }); - - expect(component?.is(Fragment)).toBeTruthy(); - expect(component?.html()).toBe(''); - }); - - test('renders nothing if keys are enabled', async () => { - http.get.mockResolvedValue({ isSufficientlySecure: true }); - - let component: ShallowWrapper | undefined; - await act(async () => { - component = shallow( - - ); - }); - - expect(component?.is(Fragment)).toBeTruthy(); - expect(component?.html()).toBe(''); - }); - - test('renders the callout if keys are disabled', async () => { - http.get.mockResolvedValue({ isSufficientlySecure: false }); - - let component: ShallowWrapper | undefined; - await act(async () => { - component = shallow( - - ); - }); - - expect(component?.find(EuiCallOut).prop('title')).toMatchInlineSnapshot( - `"Alert creation requires TLS between Elasticsearch and Kibana."` - ); - - expect(component?.find(EuiButton).prop('href')).toMatchInlineSnapshot( - `"elastic.co/guide/en/kibana/current/configuring-tls.html"` - ); - }); -}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/alert_action_security_call_out.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/alert_action_security_call_out.tsx deleted file mode 100644 index f7a80202dff89..0000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/alert_action_security_call_out.tsx +++ /dev/null @@ -1,78 +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, { Fragment } from 'react'; -import { Option, none, some, fold, filter } from 'fp-ts/lib/Option'; -import { pipe } from 'fp-ts/lib/pipeable'; - -import { EuiCallOut, EuiButton, EuiSpacer } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; - -import { DocLinksStart, HttpSetup } from 'kibana/public'; -import { AlertingFrameworkHealth } from '../../types'; -import { health } from '../lib/alert_api'; - -interface Props { - docLinks: Pick; - action: string; - http: HttpSetup; -} - -export const AlertActionSecurityCallOut: React.FunctionComponent = ({ - http, - action, - docLinks, -}) => { - const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = docLinks; - - const [alertingHealth, setAlertingHealth] = React.useState>(none); - - React.useEffect(() => { - async function fetchSecurityConfigured() { - setAlertingHealth(some(await health({ http }))); - } - - fetchSecurityConfigured(); - }, [http]); - - return pipe( - alertingHealth, - filter(healthCheck => !healthCheck.isSufficientlySecure), - fold( - () => , - () => ( - - - - - - - - - ) - ) - ); -}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email.test.tsx index a7d479f922ed1..af9e34071fd09 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email.test.tsx @@ -39,6 +39,7 @@ describe('connector validation', () => { id: 'test', actionTypeId: '.email', name: 'email', + isPreconfigured: false, config: { from: 'test@test.com', port: 2323, @@ -66,6 +67,7 @@ describe('connector validation', () => { }, id: 'test', actionTypeId: '.email', + isPreconfigured: false, name: 'email', config: { from: 'test@test.com', @@ -117,6 +119,7 @@ describe('connector validation', () => { }, id: 'test', actionTypeId: '.email', + isPreconfigured: false, name: 'email', config: { from: 'test@test.com', @@ -144,6 +147,7 @@ describe('connector validation', () => { }, id: 'test', actionTypeId: '.email', + isPreconfigured: false, name: 'email', config: { from: 'test@test.com', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.test.tsx index 6ce954f61bcdb..d83396f200829 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.test.tsx @@ -134,7 +134,7 @@ describe('IndexParamsFields renders', () => { ActionParamsProps >; const actionParams = { - documents: ['test'], + documents: [{ test: 123 }], }; const wrapper = mountWithIntl( { .find('[data-test-subj="actionIndexDoc"]') .first() .prop('value') - ).toBe('"test"'); + ).toBe(`{ + "test": 123 +}`); + expect( + wrapper.find('[data-test-subj="indexDocumentAddVariableButton"]').length > 0 + ).toBeTruthy(); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.tsx index d631882e1f581..56d9f40e40021 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index.tsx @@ -14,6 +14,10 @@ import { EuiSelect, EuiTitle, EuiIconTip, + EuiPopover, + EuiButtonIcon, + EuiContextMenuPanel, + EuiContextMenuItem, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; @@ -31,6 +35,7 @@ import { getIndexOptions, getIndexPatterns, } from '../../../common/index_controls'; +import { useXJsonMode } from '../../lib/use_x_json_mode'; export function getActionType(): ActionTypeModel { return { @@ -271,9 +276,29 @@ const IndexParamsFields: React.FunctionComponent { const { documents } = actionParams; - + const { xJsonMode, convertToJson, setXJson, xJson } = useXJsonMode( + documents && documents.length > 0 ? documents[0] : null + ); + const [isVariablesPopoverOpen, setIsVariablesPopoverOpen] = useState(false); + const messageVariablesItems = messageVariables?.map((variable: string, i: number) => ( + { + const value = (xJson ?? '').concat(` {{${variable}}}`); + setXJson(value); + // Keep the documents in sync with the editor content + onDocumentsChange(convertToJson(value)); + setIsVariablesPopoverOpen(false); + }} + > + {`{{${variable}}}`} + + )); function onDocumentsChange(updatedDocuments: string) { try { const documentsJSON = JSON.parse(updatedDocuments); @@ -291,27 +316,55 @@ const IndexParamsFields: React.FunctionComponent setIsVariablesPopoverOpen(true)} + iconType="indexOpen" + title={i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.addVariableTitle', + { + defaultMessage: 'Add variable', + } + )} + aria-label={i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.addVariablePopoverButton', + { + defaultMessage: 'Add variable', + } + )} + /> + } + isOpen={isVariablesPopoverOpen} + closePopover={() => setIsVariablesPopoverOpen(false)} + panelPaddingSize="none" + anchorPosition="downLeft" + > + + + } > 0 ? documents[0] : {}, null, 2)} - onChange={onDocumentsChange} - width="100%" - height="auto" - minLines={6} - maxLines={30} - isReadOnly={false} - setOptions={{ - showLineNumbers: true, - tabSize: 2, - }} - editorProps={{ - $blockScrolling: Infinity, + aria-label={i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.indexAction.jsonDocAriaLabel', + { + defaultMessage: 'Code editor', + } + )} + value={xJson} + onChange={(xjson: string) => { + setXJson(xjson); + // Keep the documents in sync with the editor content + onDocumentsChange(convertToJson(xjson)); }} - showGutter={true} /> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/types.ts index fd35db4304275..84d8b6e8caede 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/types.ts @@ -39,7 +39,7 @@ export interface PagerDutyActionParams { } export interface IndexActionParams { - documents: string[]; + documents: Array>; } export enum ServerLogLevelOptions { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook.test.tsx index fdb60bd2d9146..c4489a316d2c0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook.test.tsx @@ -39,6 +39,7 @@ describe('webhook connector validation', () => { id: 'test', actionTypeId: '.webhook', name: 'webhook', + isPreconfigured: false, config: { method: 'PUT', url: 'http:\\test', @@ -106,6 +107,7 @@ describe('WebhookActionConnectorFields renders', () => { }, id: 'test', actionTypeId: '.webhook', + isPreconfigured: false, name: 'webhook', config: { method: 'PUT', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.scss b/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.scss new file mode 100644 index 0000000000000..c4d12221e3a01 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.scss @@ -0,0 +1,13 @@ +@mixin padBannerWith($size) { + padding-left: $size; + padding-right: $size; +} + +.alertingHealthCheck__body { + @include padBannerWith(2 * $euiSize); +} + +.alertingFlyoutHealthCheck__body { + @include padBannerWith(2 * $euiSize); + margin-top: $euiSize; +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.test.tsx new file mode 100644 index 0000000000000..5156a6146f3a1 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.test.tsx @@ -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 React from 'react'; +import { render } from '@testing-library/react'; + +import { HealthCheck } from './health_check'; + +import { act } from 'react-dom/test-utils'; +import { httpServiceMock } from '../../../../../../src/core/public/mocks'; + +const docLinks = { ELASTIC_WEBSITE_URL: 'elastic.co/', DOC_LINK_VERSION: 'current' }; + +const http = httpServiceMock.createStartContract(); + +describe('health check', () => { + test('renders spinner while health is loading', async () => { + http.get.mockImplementationOnce(() => new Promise(() => {})); + + const { queryByText, container } = render( + +

{'shouldnt render'}

+
+ ); + await act(async () => { + // wait for useEffect to run + }); + + expect(container.getElementsByClassName('euiLoadingSpinner').length).toBe(1); + expect(queryByText('shouldnt render')).not.toBeInTheDocument(); + }); + + it('renders children if keys are enabled', async () => { + http.get.mockResolvedValue({ isSufficientlySecure: true, hasPermanentEncryptionKey: true }); + + const { queryByText } = render( + +

{'should render'}

+
+ ); + await act(async () => { + // wait for useEffect to run + }); + expect(queryByText('should render')).toBeInTheDocument(); + }); + + test('renders warning if keys are disabled', async () => { + http.get.mockImplementationOnce(async () => ({ + isSufficientlySecure: false, + hasPermanentEncryptionKey: true, + })); + + const { queryAllByText } = render( + +

{'should render'}

+
+ ); + await act(async () => { + // wait for useEffect to run + }); + + const [description, action] = queryAllByText(/TLS/i); + + expect(description.textContent).toMatchInlineSnapshot( + `"Alerting relies on API keys, which require TLS between Elasticsearch and Kibana. Learn how to enable TLS."` + ); + + expect(action.textContent).toMatchInlineSnapshot(`"Learn how to enable TLS."`); + + expect(action.getAttribute('href')).toMatchInlineSnapshot( + `"elastic.co/guide/en/kibana/current/configuring-tls.html"` + ); + }); + + test('renders warning if encryption key is ephemeral', async () => { + http.get.mockImplementationOnce(async () => ({ + isSufficientlySecure: true, + hasPermanentEncryptionKey: false, + })); + + const { queryByText, queryByRole } = render( + +

{'should render'}

+
+ ); + await act(async () => { + // wait for useEffect to run + }); + + const description = queryByRole(/banner/i); + expect(description!.textContent).toMatchInlineSnapshot( + `"To create an alert, set a value for xpack.encrypted_saved_objects.encryptionKey in your kibana.yml file. Learn how."` + ); + + const action = queryByText(/Learn/i); + expect(action!.textContent).toMatchInlineSnapshot(`"Learn how."`); + expect(action!.getAttribute('href')).toMatchInlineSnapshot( + `"elastic.co/guide/en/kibana/current/alert-action-settings-kb.html#general-alert-action-settings"` + ); + }); + + test('renders warning if encryption key is ephemeral and keys are disabled', async () => { + http.get.mockImplementationOnce(async () => ({ + isSufficientlySecure: false, + hasPermanentEncryptionKey: false, + })); + + const { queryByText } = render( + +

{'should render'}

+
+ ); + await act(async () => { + // wait for useEffect to run + }); + + const description = queryByText(/Transport Layer Security/i); + + expect(description!.textContent).toMatchInlineSnapshot( + `"You must enable Transport Layer Security between Kibana and Elasticsearch and configure an encryption key in your kibana.yml file. Learn how"` + ); + + const action = queryByText(/Learn/i); + expect(action!.textContent).toMatchInlineSnapshot(`"Learn how"`); + expect(action!.getAttribute('href')).toMatchInlineSnapshot( + `"elastic.co/guide/en/kibana/current/alerting-getting-started.html#alerting-setup-prerequisites"` + ); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.tsx new file mode 100644 index 0000000000000..c967cf5de0771 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/health_check.tsx @@ -0,0 +1,197 @@ +/* + * 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, { Fragment } from 'react'; +import { Option, none, some, fold } from 'fp-ts/lib/Option'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { FormattedMessage } from '@kbn/i18n/react'; + +import { EuiLink, EuiLoadingSpinner } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import { DocLinksStart, HttpSetup } from 'kibana/public'; + +import { EuiEmptyPrompt, EuiCode } from '@elastic/eui'; +import { AlertingFrameworkHealth } from '../../types'; +import { health } from '../lib/alert_api'; +import './health_check.scss'; + +interface Props { + docLinks: Pick; + http: HttpSetup; + inFlyout?: boolean; +} + +export const HealthCheck: React.FunctionComponent = ({ + docLinks, + http, + children, + inFlyout = false, +}) => { + const [alertingHealth, setAlertingHealth] = React.useState>(none); + + React.useEffect(() => { + (async function() { + setAlertingHealth(some(await health({ http }))); + })(); + }, [http]); + + const className = inFlyout ? 'alertingFlyoutHealthCheck' : 'alertingHealthCheck'; + + return pipe( + alertingHealth, + fold( + () => , + healthCheck => { + return healthCheck?.isSufficientlySecure && healthCheck?.hasPermanentEncryptionKey ? ( + {children} + ) : !healthCheck.isSufficientlySecure && !healthCheck.hasPermanentEncryptionKey ? ( + + ) : !healthCheck.hasPermanentEncryptionKey ? ( + + ) : ( + + ); + } + ) + ); +}; + +type PromptErrorProps = Pick & { + className?: string; +}; + +const TlsAndEncryptionError = ({ + docLinks: { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION }, + className, +}: PromptErrorProps) => ( + + + + } + body={ +
+

+ {i18n.translate('xpack.triggersActionsUI.components.healthCheck.tlsAndEncryptionError', { + defaultMessage: + 'You must enable Transport Layer Security between Kibana and Elasticsearch and configure an encryption key in your kibana.yml file. ', + })} + + {i18n.translate( + 'xpack.triggersActionsUI.components.healthCheck.tlsAndEncryptionErrorAction', + { + defaultMessage: 'Learn how', + } + )} + +

+
+ } + /> +); + +const EncryptionError = ({ + docLinks: { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION }, + className, +}: PromptErrorProps) => ( + + + + } + body={ +
+

+ {i18n.translate( + 'xpack.triggersActionsUI.components.healthCheck.encryptionErrorBeforeKey', + { + defaultMessage: 'To create an alert, set a value for ', + } + )} + {'xpack.encrypted_saved_objects.encryptionKey'} + {i18n.translate( + 'xpack.triggersActionsUI.components.healthCheck.encryptionErrorAfterKey', + { + defaultMessage: ' in your kibana.yml file. ', + } + )} + + {i18n.translate( + 'xpack.triggersActionsUI.components.healthCheck.encryptionErrorAction', + { + defaultMessage: 'Learn how.', + } + )} + +

+
+ } + /> +); + +const TlsError = ({ + docLinks: { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION }, + className, +}: PromptErrorProps) => ( + + + + } + body={ +
+

+ {i18n.translate('xpack.triggersActionsUI.components.healthCheck.tlsError', { + defaultMessage: + 'Alerting relies on API keys, which require TLS between Elasticsearch and Kibana. ', + })} + + {i18n.translate('xpack.triggersActionsUI.components.healthCheck.tlsErrorAction', { + defaultMessage: 'Learn how to enable TLS.', + })} + +

+
+ } + /> +); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/prompts/empty_connectors_prompt.scss b/x-pack/plugins/triggers_actions_ui/public/application/components/prompts/empty_connectors_prompt.scss new file mode 100644 index 0000000000000..fe001ce294ef4 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/prompts/empty_connectors_prompt.scss @@ -0,0 +1,3 @@ +.actEmptyConnectorsPrompt__logo + .actEmptyConnectorsPrompt__logo { + margin-left: $euiSize; +} \ No newline at end of file diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/prompts/empty_connectors_prompt.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/prompts/empty_connectors_prompt.tsx new file mode 100644 index 0000000000000..0e956ea56faa9 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/prompts/empty_connectors_prompt.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 { FormattedMessage } from '@kbn/i18n/react'; +import React, { Fragment } from 'react'; +import { EuiButton, EuiEmptyPrompt, EuiIcon, EuiSpacer, EuiTitle } from '@elastic/eui'; +import './empty_connectors_prompt.scss'; + +export const EmptyConnectorsPrompt = ({ onCTAClicked }: { onCTAClicked: () => void }) => ( + + + + + + +

+ +

+
+ + } + body={ +

+ +

+ } + actions={ + + + + } + /> +); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/prompts/empty_prompt.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/prompts/empty_prompt.tsx new file mode 100644 index 0000000000000..df593d587de3f --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/prompts/empty_prompt.tsx @@ -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 { FormattedMessage } from '@kbn/i18n/react'; +import React from 'react'; +import { EuiButton, EuiEmptyPrompt } from '@elastic/eui'; + +export const EmptyPrompt = ({ onCTAClicked }: { onCTAClicked: () => void }) => ( + + + + } + body={ +

+ +

+ } + actions={ + + + + } + /> +); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/security_call_out.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/security_call_out.test.tsx deleted file mode 100644 index 28bc02ec3392f..0000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/security_call_out.test.tsx +++ /dev/null @@ -1,72 +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, { Fragment } from 'react'; -import { shallow, ShallowWrapper } from 'enzyme'; -import { SecurityEnabledCallOut } from './security_call_out'; - -import { EuiCallOut, EuiButton } from '@elastic/eui'; -import { act } from 'react-dom/test-utils'; -import { httpServiceMock } from '../../../../../../src/core/public/mocks'; - -const docLinks = { ELASTIC_WEBSITE_URL: 'elastic.co/', DOC_LINK_VERSION: 'current' }; - -const http = httpServiceMock.createStartContract(); - -describe('security call out', () => { - let useEffect: any; - - const mockUseEffect = () => { - // make react execute useEffects despite shallow rendering - useEffect.mockImplementationOnce((f: Function) => f()); - }; - - beforeEach(() => { - jest.resetAllMocks(); - useEffect = jest.spyOn(React, 'useEffect'); - mockUseEffect(); - }); - - test('renders nothing while health is loading', async () => { - http.get.mockImplementationOnce(() => new Promise(() => {})); - - let component: ShallowWrapper | undefined; - await act(async () => { - component = shallow(); - }); - - expect(component?.is(Fragment)).toBeTruthy(); - expect(component?.html()).toBe(''); - }); - - test('renders nothing if keys are enabled', async () => { - http.get.mockResolvedValue({ isSufficientlySecure: true }); - - let component: ShallowWrapper | undefined; - await act(async () => { - component = shallow(); - }); - - expect(component?.is(Fragment)).toBeTruthy(); - expect(component?.html()).toBe(''); - }); - - test('renders the callout if keys are disabled', async () => { - http.get.mockImplementationOnce(async () => ({ isSufficientlySecure: false })); - - let component: ShallowWrapper | undefined; - await act(async () => { - component = shallow(); - }); - - expect(component?.find(EuiCallOut).prop('title')).toMatchInlineSnapshot( - `"Enable Transport Layer Security"` - ); - - expect(component?.find(EuiButton).prop('href')).toMatchInlineSnapshot( - `"elastic.co/guide/en/kibana/current/configuring-tls.html"` - ); - }); -}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/security_call_out.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/security_call_out.tsx deleted file mode 100644 index 9874a3a0697d2..0000000000000 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/security_call_out.tsx +++ /dev/null @@ -1,75 +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, { Fragment } from 'react'; -import { Option, none, some, fold, filter } from 'fp-ts/lib/Option'; -import { pipe } from 'fp-ts/lib/pipeable'; - -import { EuiCallOut, EuiButton, EuiSpacer } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; - -import { DocLinksStart, HttpSetup } from 'kibana/public'; - -import { AlertingFrameworkHealth } from '../../types'; -import { health } from '../lib/alert_api'; - -interface Props { - docLinks: Pick; - http: HttpSetup; -} - -export const SecurityEnabledCallOut: React.FunctionComponent = ({ docLinks, http }) => { - const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = docLinks; - - const [alertingHealth, setAlertingHealth] = React.useState>(none); - - React.useEffect(() => { - async function fetchSecurityConfigured() { - setAlertingHealth(some(await health({ http }))); - } - - fetchSecurityConfigured(); - }, [http]); - - return pipe( - alertingHealth, - filter(healthCheck => !healthCheck?.isSufficientlySecure), - fold( - () => , - () => ( - - -

- -

- - - -
- -
- ) - ) - ); -}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/home.tsx b/x-pack/plugins/triggers_actions_ui/public/application/home.tsx index 7c8d798984bf2..4d0a9980f2231 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/home.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/home.tsx @@ -29,8 +29,8 @@ import { hasShowActionsCapability, hasShowAlertsCapability } from './lib/capabil import { ActionsConnectorsList } from './sections/actions_connectors_list/components/actions_connectors_list'; import { AlertsList } from './sections/alerts_list/components/alerts_list'; -import { SecurityEnabledCallOut } from './components/security_call_out'; import { PLUGIN } from './constants/plugin'; +import { HealthCheck } from './components/health_check'; interface MatchParams { section: Section; @@ -88,7 +88,6 @@ export const TriggersActionsUIHome: React.FunctionComponent - @@ -142,9 +141,27 @@ export const TriggersActionsUIHome: React.FunctionComponent {canShowActions && ( - + ( + + + + )} + /> + )} + {canShowAlerts && ( + ( + + + + )} + /> )} - {canShowAlerts && } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.test.ts index ee68b7e269c34..e9cf2a270d180 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.test.ts @@ -43,27 +43,14 @@ describe('loadActionTypes', () => { }); describe('loadAllActions', () => { - test('should call find actions API', async () => { - const resolvedValue = { - page: 1, - perPage: 10000, - total: 0, - data: [], - }; - http.get.mockResolvedValueOnce(resolvedValue); + test('should call getAll actions API', async () => { + http.get.mockResolvedValueOnce([]); const result = await loadAllActions({ http }); - expect(result).toEqual(resolvedValue); + expect(result).toEqual([]); expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` Array [ - "/api/action/_find", - Object { - "query": Object { - "per_page": 10000, - "sort_field": "name.keyword", - "sort_order": "asc", - }, - }, + "/api/action/_getAll", ] `); }); @@ -73,6 +60,7 @@ describe('createActionConnector', () => { test('should call create action API', async () => { const connector: ActionConnectorWithoutId = { actionTypeId: 'test', + isPreconfigured: false, name: 'My test', config: {}, secrets: {}, @@ -86,7 +74,7 @@ describe('createActionConnector', () => { Array [ "/api/action", Object { - "body": "{\\"actionTypeId\\":\\"test\\",\\"name\\":\\"My test\\",\\"config\\":{},\\"secrets\\":{}}", + "body": "{\\"actionTypeId\\":\\"test\\",\\"isPreconfigured\\":false,\\"name\\":\\"My test\\",\\"config\\":{},\\"secrets\\":{}}", }, ] `); @@ -98,6 +86,7 @@ describe('updateActionConnector', () => { const id = '123'; const connector: ActionConnectorWithoutId = { actionTypeId: 'test', + isPreconfigured: false, name: 'My test', config: {}, secrets: {}, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.ts index 26ad97f05849d..e82d268accdd8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.ts @@ -8,32 +8,12 @@ import { HttpSetup } from 'kibana/public'; import { BASE_ACTION_API_PATH } from '../constants'; import { ActionConnector, ActionConnectorWithoutId, ActionType } from '../../types'; -// We are assuming there won't be many actions. This is why we will load -// all the actions in advance and assume the total count to not go over 100 or so. -// We'll set this max setting assuming it's never reached. -const MAX_ACTIONS_RETURNED = 10000; - export async function loadActionTypes({ http }: { http: HttpSetup }): Promise { return await http.get(`${BASE_ACTION_API_PATH}/types`); } -export async function loadAllActions({ - http, -}: { - http: HttpSetup; -}): Promise<{ - page: number; - perPage: number; - total: number; - data: ActionConnector[]; -}> { - return await http.get(`${BASE_ACTION_API_PATH}/_find`, { - query: { - per_page: MAX_ACTIONS_RETURNED, - sort_field: 'name.keyword', - sort_order: 'asc', - }, - }); +export async function loadAllActions({ http }: { http: HttpSetup }): Promise { + return await http.get(`${BASE_ACTION_API_PATH}/_getAll`); } export async function createActionConnector({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/use_x_json_mode.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/use_x_json_mode.ts new file mode 100644 index 0000000000000..b677924724ffe --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/use_x_json_mode.ts @@ -0,0 +1,25 @@ +/* + * 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 { useState } from 'react'; +import { XJsonMode } from '../../../../../../src/plugins/es_ui_shared/console_lang/ace/modes/x_json'; +import { + collapseLiteralStrings, + expandLiteralStrings, +} from '../../../../../../src/plugins/es_ui_shared/console_lang/lib'; +// @ts-ignore +export const xJsonMode = new XJsonMode(); +export const useXJsonMode = (json: Record | null) => { + const [xJson, setXJson] = useState( + json === null ? '' : expandLiteralStrings(JSON.stringify(json, null, 2)) + ); + + return { + xJson, + setXJson, + xJsonMode, + convertToJson: collapseLiteralStrings, + }; +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx index 89d37c4d00a11..41564146bb84d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.test.tsx @@ -11,6 +11,10 @@ import { act } from 'react-dom/test-utils'; import { actionTypeRegistryMock } from '../../action_type_registry.mock'; import { ValidationResult, Alert, AlertAction } from '../../../types'; import { ActionForm } from './action_form'; +jest.mock('../../lib/action_connector_api', () => ({ + loadAllActions: jest.fn(), + loadActionTypes: jest.fn(), +})); const actionTypeRegistry = actionTypeRegistryMock.create(); describe('action_form', () => { let deps: any; @@ -73,6 +77,17 @@ describe('action_form', () => { let wrapper: ReactWrapper; async function setup() { + const { loadAllActions } = jest.requireMock('../../lib/action_connector_api'); + loadAllActions.mockResolvedValueOnce([ + { + secrets: {}, + id: 'test', + actionTypeId: actionType.id, + name: 'Test connector', + config: {}, + isPreconfigured: false, + }, + ]); const mockes = coreMock.createSetup(); deps = { toastNotifications: mockes.notifications.toasts, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx index 3ade4e6368f96..6b011ac84bc6f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx @@ -129,7 +129,7 @@ export const ActionForm = ({ async function loadConnectors() { try { const actionsResponse = await loadAllActions({ http }); - setConnectors(actionsResponse.data); + setConnectors(actionsResponse); } catch (e) { toastNotifications.addDanger({ title: i18n.translate( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.test.tsx index f9aa2cad8bfc6..2c063ea6b4fa6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.test.tsx @@ -47,6 +47,7 @@ describe('connector_edit_flyout', () => { actionTypeId: 'test-action-type-id', actionType: 'test-action-type-name', name: 'action-connector', + isPreconfigured: false, referencedByCount: 0, config: {}, }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_reducer.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_reducer.test.ts index df7e5d8fe9a78..e469a50108912 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_reducer.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_reducer.test.ts @@ -15,6 +15,7 @@ describe('connector reducer', () => { actionTypeId: 'test-action-type-id', name: 'action-connector', referencedByCount: 0, + isPreconfigured: false, config: {}, }; }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.scss b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.scss index 3d65b8a799b1b..70ad1cae6c1d1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.scss +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.scss @@ -1,7 +1,3 @@ -.actConnectorsList__logo + .actConnectorsList__logo { - margin-left: $euiSize; -} - .actConnectorsList__tableRowDisabled { background-color: $euiColorLightestShade; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx index 9331fe1704694..4fa1e7e4c6e4d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.test.tsx @@ -29,12 +29,7 @@ describe('actions_connectors_list component empty', () => { const { loadAllActions, loadActionTypes } = jest.requireMock( '../../../lib/action_connector_api' ); - loadAllActions.mockResolvedValueOnce({ - page: 1, - perPage: 10000, - total: 0, - data: [], - }); + loadAllActions.mockResolvedValueOnce([]); loadActionTypes.mockResolvedValueOnce([ { id: 'test', @@ -111,27 +106,22 @@ describe('actions_connectors_list component with items', () => { const { loadAllActions, loadActionTypes } = jest.requireMock( '../../../lib/action_connector_api' ); - loadAllActions.mockResolvedValueOnce({ - page: 1, - perPage: 10000, - total: 2, - data: [ - { - id: '1', - actionTypeId: 'test', - description: 'My test', - referencedByCount: 1, - config: {}, - }, - { - id: '2', - actionTypeId: 'test2', - description: 'My test 2', - referencedByCount: 1, - config: {}, - }, - ], - }); + loadAllActions.mockResolvedValueOnce([ + { + id: '1', + actionTypeId: 'test', + description: 'My test', + referencedByCount: 1, + config: {}, + }, + { + id: '2', + actionTypeId: 'test2', + description: 'My test 2', + referencedByCount: 1, + config: {}, + }, + ]); loadActionTypes.mockResolvedValueOnce([ { id: 'test', @@ -214,12 +204,7 @@ describe('actions_connectors_list component empty with show only capability', () const { loadAllActions, loadActionTypes } = jest.requireMock( '../../../lib/action_connector_api' ); - loadAllActions.mockResolvedValueOnce({ - page: 1, - perPage: 10000, - total: 0, - data: [], - }); + loadAllActions.mockResolvedValueOnce([]); loadActionTypes.mockResolvedValueOnce([ { id: 'test', @@ -289,27 +274,22 @@ describe('actions_connectors_list with show only capability', () => { const { loadAllActions, loadActionTypes } = jest.requireMock( '../../../lib/action_connector_api' ); - loadAllActions.mockResolvedValueOnce({ - page: 1, - perPage: 10000, - total: 2, - data: [ - { - id: '1', - actionTypeId: 'test', - description: 'My test', - referencedByCount: 1, - config: {}, - }, - { - id: '2', - actionTypeId: 'test2', - description: 'My test 2', - referencedByCount: 1, - config: {}, - }, - ], - }); + loadAllActions.mockResolvedValueOnce([ + { + id: '1', + actionTypeId: 'test', + description: 'My test', + referencedByCount: 1, + config: {}, + }, + { + id: '2', + actionTypeId: 'test2', + description: 'My test 2', + referencedByCount: 1, + config: {}, + }, + ]); loadActionTypes.mockResolvedValueOnce([ { id: 'test', @@ -384,27 +364,22 @@ describe('actions_connectors_list component with disabled items', () => { const { loadAllActions, loadActionTypes } = jest.requireMock( '../../../lib/action_connector_api' ); - loadAllActions.mockResolvedValueOnce({ - page: 1, - perPage: 10000, - total: 2, - data: [ - { - id: '1', - actionTypeId: 'test', - description: 'My test', - referencedByCount: 1, - config: {}, - }, - { - id: '2', - actionTypeId: 'test2', - description: 'My test 2', - referencedByCount: 1, - config: {}, - }, - ], - }); + loadAllActions.mockResolvedValueOnce([ + { + id: '1', + actionTypeId: 'test', + description: 'My test', + referencedByCount: 1, + config: {}, + }, + { + id: '2', + actionTypeId: 'test2', + description: 'My test 2', + referencedByCount: 1, + config: {}, + }, + ]); loadActionTypes.mockResolvedValueOnce([ { id: 'test', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx index fc07171347e5e..47e058f473946 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/actions_connectors_list.tsx @@ -10,9 +10,6 @@ import { EuiInMemoryTable, EuiSpacer, EuiButton, - EuiIcon, - EuiEmptyPrompt, - EuiTitle, EuiLink, EuiLoadingSpinner, EuiIconTip, @@ -30,6 +27,7 @@ import { ActionsConnectorsContextProvider } from '../../../context/actions_conne import { checkActionTypeEnabled } from '../../../lib/check_action_type_enabled'; import './actions_connectors_list.scss'; import { ActionConnector, ActionConnectorTableItem, ActionTypeIndex } from '../../../../types'; +import { EmptyConnectorsPrompt } from '../../../components/prompts/empty_connectors_prompt'; export const ActionsConnectorsList: React.FunctionComponent = () => { const { http, toastNotifications, capabilities, actionTypeRegistry } = useAppDependencies(); @@ -110,7 +108,7 @@ export const ActionsConnectorsList: React.FunctionComponent = () => { setIsLoadingActions(true); try { const actionsResponse = await loadAllActions({ http }); - setActions(actionsResponse.data); + setActions(actionsResponse); } catch (e) { toastNotifications.addDanger({ title: i18n.translate( @@ -324,51 +322,6 @@ export const ActionsConnectorsList: React.FunctionComponent = () => { /> ); - const emptyPrompt = ( - - - - - - -

- -

-
-
- } - body={ -

- -

- } - actions={ - setAddFlyoutVisibility(true)} - > - - - } - /> - ); - const noPermissionPrompt = (

{ )} {data.length !== 0 && table} - {data.length === 0 && canSave && !isLoadingActions && !isLoadingActionTypes && emptyPrompt} + {data.length === 0 && canSave && !isLoadingActions && !isLoadingActionTypes && ( + setAddFlyoutVisibility(true)} /> + )} {data.length === 0 && !canSave && noPermissionPrompt} { docLinks: { ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' }, }; - mockes.http.get.mockResolvedValue({ isSufficientlySecure: true }); + mockes.http.get.mockResolvedValue({ + isSufficientlySecure: true, + hasPermanentEncryptionKey: true, + }); const alertType = { id: 'my-alert-type', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx index e44e20751b315..0620ced6365a9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import React, { useCallback, useReducer, useState } from 'react'; +import { isObject } from 'lodash'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiTitle, @@ -24,7 +25,7 @@ import { Alert, AlertAction, IErrorObject } from '../../../types'; import { AlertForm, validateBaseProperties } from './alert_form'; import { alertReducer } from './alert_reducer'; import { createAlert } from '../../lib/alert_api'; -import { AlertActionSecurityCallOut } from '../../components/alert_action_security_call_out'; +import { HealthCheck } from '../../components/health_check'; import { PLUGIN } from '../../constants/plugin'; interface AlertAddProps { @@ -83,7 +84,7 @@ export const AlertAdd = ({ ...(alertType ? alertType.validate(alert.params).errors : []), ...validateBaseProperties(alert).errors, } as IErrorObject; - const hasErrors = !!Object.keys(errors).find(errorKey => errors[errorKey].length >= 1); + const hasErrors = parseErrors(errors); const actionsErrors: Array<{ errors: IErrorObject; @@ -153,63 +154,61 @@ export const AlertAdd = ({

- - - - - - - - - {i18n.translate('xpack.triggersActionsUI.sections.alertAdd.cancelButtonLabel', { - defaultMessage: 'Cancel', - })} - - - - { - setIsSaving(true); - const savedAlert = await onSaveAlert(); - setIsSaving(false); - if (savedAlert) { - closeFlyout(); - if (reloadAlerts) { - reloadAlerts(); + + + + + + + + + {i18n.translate('xpack.triggersActionsUI.sections.alertAdd.cancelButtonLabel', { + defaultMessage: 'Cancel', + })} + + + + { + setIsSaving(true); + const savedAlert = await onSaveAlert(); + setIsSaving(false); + if (savedAlert) { + closeFlyout(); + if (reloadAlerts) { + reloadAlerts(); + } } - } - }} - > - - - - - + }} + > + + + + + + ); }; + +const parseErrors: (errors: IErrorObject) => boolean = errors => + !!Object.values(errors).find(errorList => { + if (isObject(errorList)) return parseErrors(errorList as IErrorObject); + return errorList.length >= 1; + }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx index 6fcfb463c4c77..916ba368e0732 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx @@ -36,7 +36,10 @@ describe('alert_edit', () => { docLinks: { ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' }, }; - mockedCoreSetup.http.get.mockResolvedValue({ isSufficientlySecure: true }); + mockedCoreSetup.http.get.mockResolvedValue({ + isSufficientlySecure: true, + hasPermanentEncryptionKey: true, + }); const alertType = { id: 'my-alert-type', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx index 3f27a7860bafa..4255eca83be47 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx @@ -26,7 +26,7 @@ import { Alert, AlertAction, IErrorObject } from '../../../types'; import { AlertForm, validateBaseProperties } from './alert_form'; import { alertReducer } from './alert_reducer'; import { updateAlert } from '../../lib/alert_api'; -import { AlertActionSecurityCallOut } from '../../components/alert_action_security_call_out'; +import { HealthCheck } from '../../components/health_check'; import { PLUGIN } from '../../constants/plugin'; interface AlertEditProps { @@ -137,77 +137,69 @@ export const AlertEdit = ({ - - - {hasActionsDisabled && ( - - - - - )} - - - - - - - {i18n.translate('xpack.triggersActionsUI.sections.alertEdit.cancelButtonLabel', { - defaultMessage: 'Cancel', - })} - - - - { - setIsSaving(true); - const savedAlert = await onSaveAlert(); - setIsSaving(false); - if (savedAlert) { - closeFlyout(); - if (reloadAlerts) { - reloadAlerts(); - } - } - }} - > - + + {hasActionsDisabled && ( + + - - - - + + + )} + + + + + + + {i18n.translate('xpack.triggersActionsUI.sections.alertEdit.cancelButtonLabel', { + defaultMessage: 'Cancel', + })} + + + + { + setIsSaving(true); + const savedAlert = await onSaveAlert(); + setIsSaving(false); + if (savedAlert) { + closeFlyout(); + if (reloadAlerts) { + reloadAlerts(); + } + } + }} + > + + + + + + ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx index 108cc724aa407..66aa02e1930a3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx @@ -72,12 +72,7 @@ describe('alerts_list component empty', () => { }, ]); loadAlertTypes.mockResolvedValue([{ id: 'test_alert_type', name: 'some alert type' }]); - loadAllActions.mockResolvedValue({ - page: 1, - perPage: 10000, - total: 0, - data: [], - }); + loadAllActions.mockResolvedValue([]); const mockes = coreMock.createSetup(); const [ @@ -196,12 +191,7 @@ describe('alerts_list component with items', () => { }, ]); loadAlertTypes.mockResolvedValue([{ id: 'test_alert_type', name: 'some alert type' }]); - loadAllActions.mockResolvedValue({ - page: 1, - perPage: 10000, - total: 0, - data: [], - }); + loadAllActions.mockResolvedValue([]); const mockes = coreMock.createSetup(); const [ { @@ -286,12 +276,7 @@ describe('alerts_list component empty with show only capability', () => { }, ]); loadAlertTypes.mockResolvedValue([{ id: 'test_alert_type', name: 'some alert type' }]); - loadAllActions.mockResolvedValue({ - page: 1, - perPage: 10000, - total: 0, - data: [], - }); + loadAllActions.mockResolvedValue([]); const mockes = coreMock.createSetup(); const [ { @@ -405,12 +390,7 @@ describe('alerts_list with show only capability', () => { }, ]); loadAlertTypes.mockResolvedValue([{ id: 'test_alert_type', name: 'some alert type' }]); - loadAllActions.mockResolvedValue({ - page: 1, - perPage: 10000, - total: 0, - data: [], - }); + loadAllActions.mockResolvedValue([]); const mockes = coreMock.createSetup(); const [ { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx index afd3299f0c2bb..5d59180ff572b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx @@ -15,7 +15,6 @@ import { EuiFlexItem, EuiIcon, EuiSpacer, - EuiEmptyPrompt, EuiLink, EuiLoadingSpinner, } from '@elastic/eui'; @@ -36,6 +35,7 @@ import { loadActionTypes } from '../../../lib/action_connector_api'; import { hasDeleteAlertsCapability, hasSaveAlertsCapability } from '../../../lib/capabilities'; import { routeToAlertDetails, DEFAULT_SEARCH_PAGE_SIZE } from '../../../constants'; import { DeleteModalConfirmation } from '../../../components/delete_modal_confirmation'; +import { EmptyPrompt } from '../../../components/prompts/empty_prompt'; const ENTER_KEY = 13; @@ -292,44 +292,6 @@ export const AlertsList: React.FunctionComponent = () => { ); } - const emptyPrompt = ( - - - - } - body={ -

- -

- } - actions={ - setAlertFlyoutVisibility(true)} - > - - - } - /> - ); - const table = ( @@ -473,7 +435,7 @@ export const AlertsList: React.FunctionComponent = () => { ) : ( - emptyPrompt + setAlertFlyoutVisibility(true)} /> )} void; fields: Record; customAggTypesOptions?: { diff --git a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.tsx b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.tsx index fb3ff9ceb0926..99dd7b63383fb 100644 --- a/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/common/expression_items/threshold.tsx @@ -18,11 +18,12 @@ import { } from '@elastic/eui'; import { builtInComparators } from '../constants'; import { Comparator } from '../types'; +import { IErrorObject } from '../../types'; import { ClosablePopoverTitle } from './components'; interface ThresholdExpressionProps { thresholdComparator: string; - errors: { [key: string]: string[] }; + errors: IErrorObject; onChangeSelectedThresholdComparator: (selectedThresholdComparator?: string) => void; onChangeSelectedThreshold: (selectedThreshold?: number[]) => void; customComparators?: { diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 7dfaa7b918f70..7f78d327d0122 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -66,6 +66,7 @@ export interface ActionConnector { name: string; referencedByCount?: number; config: Record; + isPreconfigured: boolean; } export type ActionConnectorWithoutId = Omit; @@ -111,5 +112,5 @@ export interface AlertTypeModel { } export interface IErrorObject { - [key: string]: string[]; + [key: string]: string | string[] | IErrorObject; } diff --git a/x-pack/plugins/watcher/public/plugin.ts b/x-pack/plugins/watcher/public/plugin.ts index 9bee7e60273e4..6de21bc27d48c 100644 --- a/x-pack/plugins/watcher/public/plugin.ts +++ b/x-pack/plugins/watcher/public/plugin.ts @@ -12,7 +12,6 @@ import { FeatureCatalogueCategory } from '../../../../src/plugins/home/public'; import { LicenseStatus } from '../common/types/license_status'; import { ILicense } from '../../licensing/public'; -import { TimeBuckets } from './legacy'; import { PLUGIN } from '../common/constants'; import { Dependencies } from './types'; @@ -41,6 +40,7 @@ export class WatcherUIPlugin implements Plugin { const [core] = await getStartServices(); const { i18n: i18nDep, docLinks, savedObjects } = core; const { boot } = await import('./application/boot'); + const { TimeBuckets } = await import('./legacy'); return boot({ // Skip the first license status, because that's already been used to determine diff --git a/x-pack/test/alerting_api_integration/common/config.ts b/x-pack/test/alerting_api_integration/common/config.ts index 5fb1afa7d584f..4d32a5ae9f53c 100644 --- a/x-pack/test/alerting_api_integration/common/config.ts +++ b/x-pack/test/alerting_api_integration/common/config.ts @@ -77,6 +77,30 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) `--xpack.actions.enabledActionTypes=${JSON.stringify(enabledActionTypes)}`, '--xpack.alerting.enabled=true', '--xpack.eventLog.logEntries=true', + `--xpack.actions.preconfigured=${JSON.stringify([ + { + id: 'my-slack1', + actionTypeId: '.slack', + name: 'Slack#xyz', + config: { + webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz', + }, + }, + { + id: 'custom-system-abc-connector', + actionTypeId: 'system-abc-action-type', + name: 'SystemABC', + config: { + xyzConfig1: 'value1', + xyzConfig2: 'value2', + listOfThings: ['a', 'b', 'c', 'd'], + }, + secrets: { + xyzSecret1: 'credential1', + xyzSecret2: 'credential2', + }, + }, + ])}`, ...disabledPlugins.map(key => `--xpack.${key}.enabled=false`), `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'alerts')}`, `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'actions')}`, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/email.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/email.ts index e228f6c1f81c6..6001dd531cfae 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/email.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/email.ts @@ -39,6 +39,7 @@ export default function emailTest({ getService }: FtrProviderContext) { createdActionId = createdAction.id; expect(createdAction).to.eql({ id: createdActionId, + isPreconfigured: false, name: 'An email action', actionTypeId: '.email', config: { @@ -58,6 +59,7 @@ export default function emailTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, + isPreconfigured: false, name: 'An email action', actionTypeId: '.email', config: { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/es_index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/es_index.ts index 01eaf92da33fe..612eba858ea0b 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/es_index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/es_index.ts @@ -40,6 +40,7 @@ export default function indexTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, + isPreconfigured: false, name: 'An index action', actionTypeId: '.index', config: { @@ -57,6 +58,7 @@ export default function indexTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, + isPreconfigured: false, name: 'An index action', actionTypeId: '.index', config: { index: ES_TEST_INDEX_NAME, refresh: false, executionTimeField: null }, @@ -79,6 +81,7 @@ export default function indexTest({ getService }: FtrProviderContext) { expect(createdActionWithIndex).to.eql({ id: createdActionWithIndex.id, + isPreconfigured: false, name: 'An index action with index config', actionTypeId: '.index', config: { @@ -96,6 +99,7 @@ export default function indexTest({ getService }: FtrProviderContext) { expect(fetchedActionWithIndex).to.eql({ id: fetchedActionWithIndex.id, + isPreconfigured: false, name: 'An index action with index config', actionTypeId: '.index', config: { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/pagerduty.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/pagerduty.ts index cfc04663c6a4f..eeb0818b5fbab 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/pagerduty.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/pagerduty.ts @@ -50,6 +50,7 @@ export default function pagerdutyTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, + isPreconfigured: false, name: 'A pagerduty action', actionTypeId: '.pagerduty', config: { @@ -65,6 +66,7 @@ export default function pagerdutyTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, + isPreconfigured: false, name: 'A pagerduty action', actionTypeId: '.pagerduty', config: { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/server_log.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/server_log.ts index f4ea568cf08c3..e9d3e6c542442 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/server_log.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/server_log.ts @@ -31,6 +31,7 @@ export default function serverLogTest({ getService }: FtrProviderContext) { serverLogActionId = createdAction.id; expect(createdAction).to.eql({ id: createdAction.id, + isPreconfigured: false, name: 'A server.log action', actionTypeId: '.server-log', config: {}, @@ -44,6 +45,7 @@ export default function serverLogTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, + isPreconfigured: false, name: 'A server.log action', actionTypeId: '.server-log', config: {}, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow.ts index 48f348e1b834d..054f8f6141817 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow.ts @@ -101,6 +101,7 @@ export default function servicenowTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, + isPreconfigured: false, name: 'A servicenow action', actionTypeId: '.servicenow', config: { @@ -117,6 +118,7 @@ export default function servicenowTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, + isPreconfigured: false, name: 'A servicenow action', actionTypeId: '.servicenow', config: { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/slack.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/slack.ts index 8afa43bfea21e..e00589b7e85b7 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/slack.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/slack.ts @@ -47,6 +47,7 @@ export default function slackTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, + isPreconfigured: false, name: 'A slack action', actionTypeId: '.slack', config: {}, @@ -60,6 +61,7 @@ export default function slackTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, + isPreconfigured: false, name: 'A slack action', actionTypeId: '.slack', config: {}, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/webhook.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/webhook.ts index da83dbf8c47e2..fd996ea4507ba 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/webhook.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/webhook.ts @@ -92,6 +92,7 @@ export default function webhookTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, + isPreconfigured: false, name: 'A generic Webhook action', actionTypeId: '.webhook', config: { @@ -108,6 +109,7 @@ export default function webhookTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, + isPreconfigured: false, name: 'A generic Webhook action', actionTypeId: '.webhook', config: { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/create.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/create.ts index 43a3861491467..922315eba5a5c 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/create.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/create.ts @@ -55,6 +55,7 @@ export default function createActionTests({ getService }: FtrProviderContext) { objectRemover.add(space.id, response.body.id, 'action'); expect(response.body).to.eql({ id: response.body.id, + isPreconfigured: false, name: 'My action', actionTypeId: 'test.index-record', config: { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/delete.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/delete.ts index 6fca330887c3e..011e47cf11b39 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/delete.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/delete.ts @@ -137,6 +137,36 @@ export default function deleteActionTests({ getService }: FtrProviderContext) { throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); } }); + + it(`shouldn't delete action from preconfigured list`, async () => { + const response = await supertestWithoutAuth + .delete(`${getUrlPrefix(space.id)}/api/action/my-slack1`) + .auth(user.username, user.password) + .set('kbn-xsrf', 'foo'); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'global_read at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(404); + expect(response.body).to.eql({ + statusCode: 404, + error: 'Not Found', + message: 'Not Found', + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: 'Preconfigured action my-slack1 is not allowed to delete.', + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); }); } }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get.ts index bed4c805aaf57..c84b089d48c85 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get.ts @@ -59,6 +59,7 @@ export default function getActionTests({ getService }: FtrProviderContext) { expect(response.statusCode).to.eql(200); expect(response.body).to.eql({ id: createdAction.id, + isPreconfigured: false, actionTypeId: 'test.index-record', name: 'My action', config: { @@ -115,6 +116,40 @@ export default function getActionTests({ getService }: FtrProviderContext) { throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); } }); + + it('should handle get preconfigured action request appropriately', async () => { + const response = await supertestWithoutAuth + .get(`${getUrlPrefix(space.id)}/api/action/my-slack1`) + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(404); + expect(response.body).to.eql({ + statusCode: 404, + error: 'Not Found', + message: 'Not Found', + }); + break; + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all at space1': + expect(response.statusCode).to.eql(200); + expect(response.body).to.eql({ + id: 'my-slack1', + actionTypeId: '.slack', + name: 'Slack#xyz', + isPreconfigured: true, + config: { + webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz', + }, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); }); } }); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/find.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get_all.ts similarity index 57% rename from x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/find.ts rename to x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get_all.ts index 89c5b4f451f82..80b512f3fb5e3 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/find.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/get_all.ts @@ -10,11 +10,11 @@ import { getUrlPrefix, getTestAlertData, ObjectRemover } from '../../../common/l import { FtrProviderContext } from '../../../common/ftr_provider_context'; // eslint-disable-next-line import/no-default-export -export default function findActionTests({ getService }: FtrProviderContext) { +export default function getAllActionTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const supertestWithoutAuth = getService('supertestWithoutAuth'); - describe('find', () => { + describe('getAll', () => { const objectRemover = new ObjectRemover(supertest); afterEach(() => objectRemover.removeAll()); @@ -22,7 +22,7 @@ export default function findActionTests({ getService }: FtrProviderContext) { for (const scenario of UserAtSpaceScenarios) { const { user, space } = scenario; describe(scenario.id, () => { - it('should handle find action request appropriately', async () => { + it('should handle get all action request appropriately', async () => { const { body: createdAction } = await supertest .post(`${getUrlPrefix(space.id)}/api/action`) .set('kbn-xsrf', 'foo') @@ -40,11 +40,7 @@ export default function findActionTests({ getService }: FtrProviderContext) { objectRemover.add(space.id, createdAction.id, 'action'); const response = await supertestWithoutAuth - .get( - `${getUrlPrefix( - space.id - )}/api/action/_find?search=test.index-record&search_fields=actionTypeId` - ) + .get(`${getUrlPrefix(space.id)}/api/action/_getAll`) .auth(user.username, user.password); switch (scenario.id) { @@ -61,90 +57,47 @@ export default function findActionTests({ getService }: FtrProviderContext) { case 'superuser at space1': case 'space_1_all at space1': expect(response.statusCode).to.eql(200); - expect(response.body).to.eql({ - page: 1, - perPage: 20, - total: 1, - data: [ - { - id: createdAction.id, - name: 'My action', - actionTypeId: 'test.index-record', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - referencedByCount: 0, + expect(response.body).to.eql([ + { + id: createdAction.id, + isPreconfigured: false, + name: 'My action', + actionTypeId: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, }, - ], - }); - break; - default: - throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); - } - }); - - it('should handle find action request with filter appropriately', async () => { - const { body: createdAction } = await supertest - .post(`${getUrlPrefix(space.id)}/api/action`) - .set('kbn-xsrf', 'foo') - .send({ - name: 'My action', - actionTypeId: 'test.index-record', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - secrets: { - encrypted: 'This value should be encrypted', - }, - }) - .expect(200); - objectRemover.add(space.id, createdAction.id, 'action'); - - const response = await supertestWithoutAuth - .get( - `${getUrlPrefix( - space.id - )}/api/action/_find?filter=action.attributes.actionTypeId:test.index-record` - ) - .auth(user.username, user.password); - - switch (scenario.id) { - case 'no_kibana_privileges at space1': - case 'space_1_all at space2': - expect(response.statusCode).to.eql(404); - expect(response.body).to.eql({ - statusCode: 404, - error: 'Not Found', - message: 'Not Found', - }); - break; - case 'global_read at space1': - case 'superuser at space1': - case 'space_1_all at space1': - expect(response.statusCode).to.eql(200); - expect(response.body).to.eql({ - page: 1, - perPage: 20, - total: 1, - data: [ - { - id: createdAction.id, - name: 'My action', - actionTypeId: 'test.index-record', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - referencedByCount: 0, + referencedByCount: 0, + }, + { + id: 'my-slack1', + isPreconfigured: true, + actionTypeId: '.slack', + name: 'Slack#xyz', + config: { + webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz', }, - ], - }); + referencedByCount: 0, + }, + { + id: 'custom-system-abc-connector', + isPreconfigured: true, + actionTypeId: 'system-abc-action-type', + name: 'SystemABC', + config: { + xyzConfig1: 'value1', + xyzConfig2: 'value2', + listOfThings: ['a', 'b', 'c', 'd'], + }, + referencedByCount: 0, + }, + ]); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); } }); - it('should handle find request appropriately with proper referencedByCount', async () => { + it('should handle get all request appropriately with proper referencedByCount', async () => { const { body: createdAction } = await supertest .post(`${getUrlPrefix(space.id)}/api/action`) .set('kbn-xsrf', 'foo') @@ -172,6 +125,13 @@ export default function findActionTests({ getService }: FtrProviderContext) { id: createdAction.id, params: {}, }, + { + group: 'default', + id: 'my-slack1', + params: { + message: 'test', + }, + }, ], }) ) @@ -179,11 +139,7 @@ export default function findActionTests({ getService }: FtrProviderContext) { objectRemover.add(space.id, createdAlert.id, 'alert'); const response = await supertestWithoutAuth - .get( - `${getUrlPrefix( - space.id - )}/api/action/_find?filter=action.attributes.actionTypeId:test.index-record` - ) + .get(`${getUrlPrefix(space.id)}/api/action/_getAll`) .auth(user.username, user.password); switch (scenario.id) { @@ -200,29 +156,47 @@ export default function findActionTests({ getService }: FtrProviderContext) { case 'superuser at space1': case 'space_1_all at space1': expect(response.statusCode).to.eql(200); - expect(response.body).to.eql({ - page: 1, - perPage: 20, - total: 1, - data: [ - { - id: createdAction.id, - name: 'My action', - actionTypeId: 'test.index-record', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - referencedByCount: 1, + expect(response.body).to.eql([ + { + id: createdAction.id, + isPreconfigured: false, + name: 'My action', + actionTypeId: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, }, - ], - }); + referencedByCount: 1, + }, + { + id: 'my-slack1', + isPreconfigured: true, + actionTypeId: '.slack', + name: 'Slack#xyz', + config: { + webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz', + }, + referencedByCount: 1, + }, + { + id: 'custom-system-abc-connector', + isPreconfigured: true, + actionTypeId: 'system-abc-action-type', + name: 'SystemABC', + config: { + xyzConfig1: 'value1', + xyzConfig2: 'value2', + listOfThings: ['a', 'b', 'c', 'd'], + }, + referencedByCount: 0, + }, + ]); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); } }); - it(`shouldn't find action from another space`, async () => { + it(`shouldn't get actions from another space`, async () => { const { body: createdAction } = await supertest .post(`${getUrlPrefix(space.id)}/api/action`) .set('kbn-xsrf', 'foo') @@ -240,11 +214,7 @@ export default function findActionTests({ getService }: FtrProviderContext) { objectRemover.add(space.id, createdAction.id, 'action'); const response = await supertestWithoutAuth - .get( - `${getUrlPrefix( - 'other' - )}/api/action/_find?search=test.index-record&search_fields=actionTypeId` - ) + .get(`${getUrlPrefix('other')}/api/action/_getAll`) .auth(user.username, user.password); switch (scenario.id) { @@ -261,12 +231,30 @@ export default function findActionTests({ getService }: FtrProviderContext) { case 'global_read at space1': case 'superuser at space1': expect(response.statusCode).to.eql(200); - expect(response.body).to.eql({ - page: 1, - perPage: 20, - total: 0, - data: [], - }); + expect(response.body).to.eql([ + { + id: 'my-slack1', + isPreconfigured: true, + actionTypeId: '.slack', + name: 'Slack#xyz', + config: { + webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz', + }, + referencedByCount: 0, + }, + { + id: 'custom-system-abc-connector', + isPreconfigured: true, + actionTypeId: 'system-abc-action-type', + name: 'SystemABC', + config: { + xyzConfig1: 'value1', + xyzConfig2: 'value2', + listOfThings: ['a', 'b', 'c', 'd'], + }, + referencedByCount: 0, + }, + ]); break; default: throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/index.ts index c6960a4eedd25..d7ec2e78ccb30 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/index.ts @@ -19,7 +19,7 @@ export default function actionsTests({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./create')); loadTestFile(require.resolve('./delete')); loadTestFile(require.resolve('./execute')); - loadTestFile(require.resolve('./find')); + loadTestFile(require.resolve('./get_all')); loadTestFile(require.resolve('./get')); loadTestFile(require.resolve('./list_action_types')); loadTestFile(require.resolve('./update')); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/update.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/update.ts index a792efede07ee..6cafbeb8c6ea8 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/update.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/update.ts @@ -69,6 +69,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) { expect(response.statusCode).to.eql(200); expect(response.body).to.eql({ id: createdAction.id, + isPreconfigured: false, actionTypeId: 'test.index-record', name: 'My action updated', config: { @@ -307,6 +308,45 @@ export default function updateActionTests({ getService }: FtrProviderContext) { throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); } }); + + it(`shouldn't update action from preconfigured list`, async () => { + const response = await supertestWithoutAuth + .put(`${getUrlPrefix(space.id)}/api/action/custom-system-abc-connector`) + .auth(user.username, user.password) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action updated', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + case 'global_read at space1': + expect(response.statusCode).to.eql(404); + expect(response.body).to.eql({ + statusCode: 404, + error: 'Not Found', + message: 'Not Found', + }); + break; + case 'superuser at space1': + case 'space_1_all at space1': + expect(response.body).to.eql({ + statusCode: 400, + error: 'Bad Request', + message: `Preconfigured action custom-system-abc-connector is not allowed to update.`, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); }); } }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/es_index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/es_index.ts index 3713e9c24419f..874d42ac04736 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/es_index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/builtin_action_types/es_index.ts @@ -38,6 +38,7 @@ export default function indexTest({ getService }: FtrProviderContext) { expect(createdAction).to.eql({ id: createdAction.id, + isPreconfigured: false, name: 'An index action', actionTypeId: '.index', config: { @@ -55,6 +56,7 @@ export default function indexTest({ getService }: FtrProviderContext) { expect(fetchedAction).to.eql({ id: fetchedAction.id, + isPreconfigured: false, name: 'An index action', actionTypeId: '.index', config: { index: ES_TEST_INDEX_NAME, refresh: false, executionTimeField: null }, @@ -77,6 +79,7 @@ export default function indexTest({ getService }: FtrProviderContext) { expect(createdActionWithIndex).to.eql({ id: createdActionWithIndex.id, + isPreconfigured: false, name: 'An index action with index config', actionTypeId: '.index', config: { @@ -94,6 +97,7 @@ export default function indexTest({ getService }: FtrProviderContext) { expect(fetchedActionWithIndex).to.eql({ id: fetchedActionWithIndex.id, + isPreconfigured: false, name: 'An index action with index config', actionTypeId: '.index', config: { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/create.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/create.ts index efd707b59cd34..c70c289194abb 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/create.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/create.ts @@ -37,6 +37,7 @@ export default function createActionTests({ getService }: FtrProviderContext) { objectRemover.add(Spaces.space1.id, response.body.id, 'action'); expect(response.body).to.eql({ id: response.body.id, + isPreconfigured: false, name: 'My action', actionTypeId: 'test.index-record', config: { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/delete.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/delete.ts index 283e51352c272..26a811d2cc512 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/delete.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/delete.ts @@ -76,5 +76,16 @@ export default function deleteActionTests({ getService }: FtrProviderContext) { message: 'Saved object [action/2] not found', }); }); + + it(`shouldn't delete action from preconfigured list`, async () => { + await supertest + .delete(`${getUrlPrefix(Spaces.space1.id)}/api/action/my-slack1`) + .set('kbn-xsrf', 'foo') + .expect(400, { + statusCode: 400, + error: 'Bad Request', + message: `Preconfigured action my-slack1 is not allowed to delete.`, + }); + }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/find.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/find.ts deleted file mode 100644 index acbc9edc1f2fb..0000000000000 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/find.ts +++ /dev/null @@ -1,92 +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 { Spaces } from '../../scenarios'; -import { getUrlPrefix, ObjectRemover } from '../../../common/lib'; -import { FtrProviderContext } from '../../../common/ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default function findActionTests({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - - describe('find', () => { - const objectRemover = new ObjectRemover(supertest); - - afterEach(() => objectRemover.removeAll()); - - it('should handle find action request appropriately', async () => { - const { body: createdAction } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/action`) - .set('kbn-xsrf', 'foo') - .send({ - name: 'My action', - actionTypeId: 'test.index-record', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - secrets: { - encrypted: 'This value should be encrypted', - }, - }) - .expect(200); - objectRemover.add(Spaces.space1.id, createdAction.id, 'action'); - - await supertest - .get( - `${getUrlPrefix( - Spaces.space1.id - )}/api/action/_find?search=test.index-record&search_fields=actionTypeId` - ) - .expect(200, { - page: 1, - perPage: 20, - total: 1, - data: [ - { - id: createdAction.id, - name: 'My action', - actionTypeId: 'test.index-record', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - referencedByCount: 0, - }, - ], - }); - }); - - it(`shouldn't find action from another space`, async () => { - const { body: createdAction } = await supertest - .post(`${getUrlPrefix(Spaces.space1.id)}/api/action`) - .set('kbn-xsrf', 'foo') - .send({ - name: 'My action', - actionTypeId: 'test.index-record', - config: { - unencrypted: `This value shouldn't get encrypted`, - }, - secrets: { - encrypted: 'This value should be encrypted', - }, - }) - .expect(200); - objectRemover.add(Spaces.space1.id, createdAction.id, 'action'); - - await supertest - .get( - `${getUrlPrefix( - Spaces.other.id - )}/api/action/_find?search=test.index-record&search_fields=actionTypeId` - ) - .expect(200, { - page: 1, - perPage: 20, - total: 0, - data: [], - }); - }); - }); -} diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts index 0f896bfaa0af9..a4a13441fb766 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get.ts @@ -38,6 +38,7 @@ export default function getActionTests({ getService }: FtrProviderContext) { .get(`${getUrlPrefix(Spaces.space1.id)}/api/action/${createdAction.id}`) .expect(200, { id: createdAction.id, + isPreconfigured: false, actionTypeId: 'test.index-record', name: 'My action', config: { @@ -71,5 +72,17 @@ export default function getActionTests({ getService }: FtrProviderContext) { message: `Saved object [action/${createdAction.id}] not found`, }); }); + + it('should handle get action request from preconfigured list', async () => { + await supertest.get(`${getUrlPrefix(Spaces.space1.id)}/api/action/my-slack1`).expect(200, { + id: 'my-slack1', + isPreconfigured: true, + actionTypeId: '.slack', + name: 'Slack#xyz', + config: { + webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz', + }, + }); + }); }); } diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts new file mode 100644 index 0000000000000..517c64f178af5 --- /dev/null +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/get_all.ts @@ -0,0 +1,116 @@ +/* + * 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 { Spaces } from '../../scenarios'; +import { getUrlPrefix, ObjectRemover } from '../../../common/lib'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function getAllActionTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + describe('getAll', () => { + const objectRemover = new ObjectRemover(supertest); + + afterEach(() => objectRemover.removeAll()); + + it('should handle get all action request appropriately', async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/action`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + actionTypeId: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAction.id, 'action'); + + await supertest.get(`${getUrlPrefix(Spaces.space1.id)}/api/action/_getAll`).expect(200, [ + { + id: createdAction.id, + isPreconfigured: false, + name: 'My action', + actionTypeId: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + referencedByCount: 0, + }, + { + id: 'my-slack1', + isPreconfigured: true, + actionTypeId: '.slack', + name: 'Slack#xyz', + config: { + webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz', + }, + referencedByCount: 0, + }, + { + id: 'custom-system-abc-connector', + isPreconfigured: true, + actionTypeId: 'system-abc-action-type', + name: 'SystemABC', + config: { + xyzConfig1: 'value1', + xyzConfig2: 'value2', + listOfThings: ['a', 'b', 'c', 'd'], + }, + referencedByCount: 0, + }, + ]); + }); + + it(`shouldn't get all action from another space`, async () => { + const { body: createdAction } = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/action`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action', + actionTypeId: 'test.index-record', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(200); + objectRemover.add(Spaces.space1.id, createdAction.id, 'action'); + + await supertest.get(`${getUrlPrefix(Spaces.other.id)}/api/action/_getAll`).expect(200, [ + { + id: 'my-slack1', + isPreconfigured: true, + actionTypeId: '.slack', + name: 'Slack#xyz', + config: { + webhookUrl: 'https://hooks.slack.com/services/abcd/efgh/ijklmnopqrstuvwxyz', + }, + referencedByCount: 0, + }, + { + id: 'custom-system-abc-connector', + isPreconfigured: true, + actionTypeId: 'system-abc-action-type', + name: 'SystemABC', + config: { + xyzConfig1: 'value1', + xyzConfig2: 'value2', + listOfThings: ['a', 'b', 'c', 'd'], + }, + referencedByCount: 0, + }, + ]); + }); + }); +} diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/index.ts index fb2be8c86f4e8..75544b7fd4169 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/index.ts @@ -11,7 +11,7 @@ export default function actionsTests({ loadTestFile }: FtrProviderContext) { describe('Actions', () => { loadTestFile(require.resolve('./create')); loadTestFile(require.resolve('./delete')); - loadTestFile(require.resolve('./find')); + loadTestFile(require.resolve('./get_all')); loadTestFile(require.resolve('./get')); loadTestFile(require.resolve('./list_action_types')); loadTestFile(require.resolve('./update')); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/type_not_enabled.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/type_not_enabled.ts index 18a0ecc23c1e1..2593f342a8a86 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/type_not_enabled.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/type_not_enabled.ts @@ -63,6 +63,7 @@ export default function typeNotEnabledTests({ getService }: FtrProviderContext) actionTypeId: 'test.not-enabled', config: {}, id: 'uuid-actionId', + isPreconfigured: false, name: 'an action created before test.not-enabled was disabled', }); }); @@ -89,6 +90,7 @@ export default function typeNotEnabledTests({ getService }: FtrProviderContext) actionTypeId: 'test.not-enabled', config: {}, id: 'uuid-actionId', + isPreconfigured: false, name: 'an action created before test.not-enabled was disabled', }); }); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts index fb0c5e13c0720..05d26aaaed2ec 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/actions/update.ts @@ -48,6 +48,7 @@ export default function updateActionTests({ getService }: FtrProviderContext) { }) .expect(200, { id: createdAction.id, + isPreconfigured: false, actionTypeId: 'test.index-record', name: 'My action updated', config: { @@ -99,5 +100,25 @@ export default function updateActionTests({ getService }: FtrProviderContext) { message: `Saved object [action/${createdAction.id}] not found`, }); }); + + it(`shouldn't update action from preconfigured list`, async () => { + await supertest + .put(`${getUrlPrefix(Spaces.space1.id)}/api/action/custom-system-abc-connector`) + .set('kbn-xsrf', 'foo') + .send({ + name: 'My action updated', + config: { + unencrypted: `This value shouldn't get encrypted`, + }, + secrets: { + encrypted: 'This value should be encrypted', + }, + }) + .expect(400, { + statusCode: 400, + error: 'Bad Request', + message: `Preconfigured action custom-system-abc-connector is not allowed to update.`, + }); + }); }); } diff --git a/x-pack/test/api_integration/apis/endpoint/metadata.ts b/x-pack/test/api_integration/apis/endpoint/metadata.ts index ad495d4505404..887be6b85b100 100644 --- a/x-pack/test/api_integration/apis/endpoint/metadata.ts +++ b/x-pack/test/api_integration/apis/endpoint/metadata.ts @@ -139,7 +139,7 @@ export default function({ getService }: FtrProviderContext) { .expect(200); expect(body.total).to.eql(2); const resultIps: string[] = [].concat( - ...body.hosts.map((metadata: Record) => metadata.host.ip) + ...body.hosts.map((hostInfo: Record) => hostInfo.metadata.host.ip) ); expect(resultIps).to.eql([ '10.192.213.130', @@ -164,7 +164,7 @@ export default function({ getService }: FtrProviderContext) { .expect(200); expect(body.total).to.eql(2); const resultOsVariantValue: Set = new Set( - body.hosts.map((metadata: Record) => metadata.host.os.variant) + body.hosts.map((hostInfo: Record) => hostInfo.metadata.host.os.variant) ); expect(Array.from(resultOsVariantValue)).to.eql([variantValue]); expect(body.hosts.length).to.eql(2); @@ -182,17 +182,17 @@ export default function({ getService }: FtrProviderContext) { }) .expect(200); expect(body.total).to.eql(1); - const resultIp: string = body.hosts[0].host.ip.filter( + const resultIp: string = body.hosts[0].metadata.host.ip.filter( (ip: string) => ip === targetEndpointIp ); expect(resultIp).to.eql([targetEndpointIp]); - expect(body.hosts[0].event.created).to.eql(1579881969541); + expect(body.hosts[0].metadata.event.created).to.eql(1579881969541); expect(body.hosts.length).to.eql(1); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(0); }); - it('metadata api should return the endpoint based on the elastic agent id', async () => { + it('metadata api should return the endpoint based on the elastic agent id, and status should be error', async () => { const targetEndpointId = 'fc0ff548-feba-41b6-8367-65e8790d0eaf'; const targetElasticAgentId = '023fa40c-411d-4188-a941-4147bfadd095'; const { body } = await supertest @@ -203,11 +203,12 @@ export default function({ getService }: FtrProviderContext) { }) .expect(200); expect(body.total).to.eql(1); - const resultHostId: string = body.hosts[0].host.id; - const resultElasticAgentId: string = body.hosts[0].elastic.agent.id; + const resultHostId: string = body.hosts[0].metadata.host.id; + const resultElasticAgentId: string = body.hosts[0].metadata.elastic.agent.id; expect(resultHostId).to.eql(targetEndpointId); expect(resultElasticAgentId).to.eql(targetElasticAgentId); - expect(body.hosts[0].event.created).to.eql(1579881969541); + expect(body.hosts[0].metadata.event.created).to.eql(1579881969541); + expect(body.hosts[0].host_status).to.eql('error'); expect(body.hosts.length).to.eql(1); expect(body.request_page_size).to.eql(10); expect(body.request_page_index).to.eql(0); diff --git a/x-pack/test/api_integration/apis/management/index_lifecycle_management/policies.js b/x-pack/test/api_integration/apis/management/index_lifecycle_management/policies.js index 9cb2e5ee54596..e41dd40bbd417 100644 --- a/x-pack/test/api_integration/apis/management/index_lifecycle_management/policies.js +++ b/x-pack/test/api_integration/apis/management/index_lifecycle_management/policies.js @@ -32,7 +32,9 @@ export default function({ getService }) { after(() => Promise.all([cleanUpEsResources(), cleanUpPolicies()])); describe('list', () => { - it('should have a default policy to manage the Watcher history indices', async () => { + // Disabled as the underline ES API has changed. Need to investigate + // Opened issue: https://github.com/elastic/kibana/issues/62778 + it.skip('should have a default policy to manage the Watcher history indices', async () => { const { body } = await loadPolicies().expect(200); const policy = body.find(policy => policy.name === DEFAULT_POLICY_NAME); diff --git a/x-pack/test/api_integration/apis/security/privileges.ts b/x-pack/test/api_integration/apis/security/privileges.ts index 77293ddff3f9f..9bec3fd076e86 100644 --- a/x-pack/test/api_integration/apis/security/privileges.ts +++ b/x-pack/test/api_integration/apis/security/privileges.ts @@ -41,7 +41,7 @@ export default function({ getService }: FtrProviderContext) { }, global: ['all', 'read'], space: ['all', 'read'], - reserved: ['ml', 'monitoring'], + reserved: ['ml_user', 'ml_admin', 'monitoring'], }; await supertest diff --git a/x-pack/test/api_integration/apis/security/privileges_basic.ts b/x-pack/test/api_integration/apis/security/privileges_basic.ts index 0b29fc1cac7de..1f9eac148b302 100644 --- a/x-pack/test/api_integration/apis/security/privileges_basic.ts +++ b/x-pack/test/api_integration/apis/security/privileges_basic.ts @@ -39,7 +39,7 @@ export default function({ getService }: FtrProviderContext) { }, global: ['all', 'read'], space: ['all', 'read'], - reserved: ['ml', 'monitoring'], + reserved: ['ml_user', 'ml_admin', 'monitoring'], }; await supertest diff --git a/x-pack/test/api_integration/apis/siem/sources.ts b/x-pack/test/api_integration/apis/siem/sources.ts index 0b147022c7cd5..e0db91449a8cc 100644 --- a/x-pack/test/api_integration/apis/siem/sources.ts +++ b/x-pack/test/api_integration/apis/siem/sources.ts @@ -30,7 +30,7 @@ export default function({ getService }: FtrProviderContext) { .then(resp => { 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(395); + expect(sourceStatus.indexFields.length).to.be(397); expect(sourceStatus.indicesExist).to.be(true); }); }); diff --git a/x-pack/test/functional/config.edge.js b/x-pack/test/functional/config.edge.js new file mode 100644 index 0000000000000..882fb6fea3686 --- /dev/null +++ b/x-pack/test/functional/config.edge.js @@ -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. + */ + +export default async function({ readConfigFile }) { + const chromeConfig = await readConfigFile(require.resolve('./config')); + + return { + ...chromeConfig.getAll(), + + browser: { + type: 'msedge', + }, + + junit: { + reportName: 'MS Chromium Edge XPack UI Functional Tests', + }, + }; +} diff --git a/x-pack/test/functional/services/machine_learning/job_wizard_common.ts b/x-pack/test/functional/services/machine_learning/job_wizard_common.ts index 36181b66786d5..af33ec2301edc 100644 --- a/x-pack/test/functional/services/machine_learning/job_wizard_common.ts +++ b/x-pack/test/functional/services/machine_learning/job_wizard_common.ts @@ -330,9 +330,11 @@ export function MachineLearningJobWizardCommonProvider( await this.ensureAdvancedSectionOpen(); subj = advancedSectionSelector(subj); } - await mlCommon.setValueWithChecks(subj, modelMemoryLimit, { clearWithKeyboard: true }); - await this.assertModelMemoryLimitValue(modelMemoryLimit, { - withAdvancedSection: sectionOptions.withAdvancedSection, + await retry.tryForTime(15 * 1000, async () => { + await mlCommon.setValueWithChecks(subj, modelMemoryLimit, { clearWithKeyboard: true }); + await this.assertModelMemoryLimitValue(modelMemoryLimit, { + withAdvancedSection: sectionOptions.withAdvancedSection, + }); }); }, diff --git a/x-pack/test/functional/services/transform_ui/source_selection.ts b/x-pack/test/functional/services/transform_ui/source_selection.ts index d2ef2c67f0004..38a819e285d67 100644 --- a/x-pack/test/functional/services/transform_ui/source_selection.ts +++ b/x-pack/test/functional/services/transform_ui/source_selection.ts @@ -8,6 +8,7 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export function TransformSourceSelectionProvider({ getService }: FtrProviderContext) { const testSubjects = getService('testSubjects'); + const retry = getService('retry'); return { async assertSourceListContainsEntry(sourceName: string) { @@ -23,8 +24,10 @@ export function TransformSourceSelectionProvider({ getService }: FtrProviderCont async selectSource(sourceName: string) { await this.filterSourceSelection(sourceName); - await testSubjects.clickWhenNotDisabled(`savedObjectTitle${sourceName}`); - await testSubjects.existOrFail('transformPageCreateTransform'); + await retry.tryForTime(30 * 1000, async () => { + await testSubjects.clickWhenNotDisabled(`savedObjectTitle${sourceName}`); + await testSubjects.existOrFail('transformPageCreateTransform', { timeout: 10 * 1000 }); + }); }, }; } diff --git a/yarn.lock b/yarn.lock index 77ab69c715573..3f04b2d26a013 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2499,6 +2499,11 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== +"@sindresorhus/is@^2.0.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-2.1.0.tgz#6ad4ca610f696098e92954ab431ff83bea0ce13f" + integrity sha512-lXKXfypKo644k4Da4yXkPCrwcvn6SlUW2X2zFbuflKHNjf0w9htru01bo26uMhleMXsDmnZ12eJLdrAZa9MANg== + "@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.4.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.6.0.tgz#ec7670432ae9c8eb710400d112c201a362d83393" @@ -3398,6 +3403,13 @@ "@svgr/plugin-svgo" "^4.2.0" loader-utils "^1.2.3" +"@szmarczak/http-timer@^4.0.0": + version "4.0.5" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.5.tgz#bfbd50211e9dfa51ba07da58a14cdfd333205152" + integrity sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ== + dependencies: + defer-to-connect "^2.0.0" + "@testim/chrome-version@^1.0.7": version "1.0.7" resolved "https://registry.yarnpkg.com/@testim/chrome-version/-/chrome-version-1.0.7.tgz#0cd915785ec4190f08a3a6acc9b61fc38fb5f1a9" @@ -3646,6 +3658,16 @@ resolved "https://registry.yarnpkg.com/@types/browserslist-useragent/-/browserslist-useragent-3.0.0.tgz#d425c9818182ce71ce53866798cee9c7d41d6e53" integrity sha512-ZBvKzg3yyWNYEkwxAzdmUzp27sFvw+1m080/+2lwrt+eltNefn1f4fnpMyrjOla31p8zLleCYqQXw+3EETfn0w== +"@types/cacheable-request@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976" + integrity sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "*" + "@types/node" "*" + "@types/responselike" "*" + "@types/caseless@*": version "0.12.2" resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8" @@ -4015,6 +4037,11 @@ "@types/react" "*" hoist-non-react-statics "^3.3.0" +"@types/http-cache-semantics@*": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a" + integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A== + "@types/indent-string@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/indent-string/-/indent-string-3.0.0.tgz#9ebb391ceda548926f5819ad16405349641b999f" @@ -4146,6 +4173,13 @@ dependencies: "@types/node" "*" +"@types/keyv@*": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7" + integrity sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw== + dependencies: + "@types/node" "*" + "@types/license-checker@15.0.0": version "15.0.0" resolved "https://registry.yarnpkg.com/@types/license-checker/-/license-checker-15.0.0.tgz#685d69e2cf61ffd862320434601f51c85e28bba1" @@ -4617,6 +4651,13 @@ "@types/tough-cookie" "*" form-data "^2.5.0" +"@types/responselike@*": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" + integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + dependencies: + "@types/node" "*" + "@types/retry@^0.12.0": version "0.12.0" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" @@ -4632,10 +4673,10 @@ resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-2.4.28.tgz#9ce8fa048c1e8c85cb71d7fe4d704e000226036f" integrity sha512-SMA+fUwULwK7sd/ZJicUztiPs8F1yCPwF3O23Z9uQ32ME5Ha0NmDK9+QTsYE4O2tHXChzXomSWWeIhCnoN1LqA== -"@types/selenium-webdriver@^4.0.5": - version "4.0.5" - resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-4.0.5.tgz#23041a4948c82daf2df9836e4d2358fec10d3e24" - integrity sha512-ma1aL1znI3ptEbSQgbywgadrRCJouPIACSfOl/bPwu/TPNSyyE/+o9jZ6+bpDVTtIdksZuVKpq4SR1ip3DRduw== +"@types/selenium-webdriver@4.0.9": + version "4.0.9" + resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-4.0.9.tgz#12621e55b2ef8f6c98bd17fe23fa720c6cba16bd" + integrity sha512-HopIwBE7GUXsscmt/J0DhnFXLSmO04AfxT6b8HAprknwka7pqEWquWDMXxCjd+NUHK9MkCe1SDKKsMiNmCItbQ== "@types/semver@^5.5.0": version "5.5.0" @@ -7358,13 +7399,18 @@ binaryextensions@2: resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-2.1.1.tgz#3209a51ca4a4ad541a3b8d3d6a6d5b83a2485935" integrity sha512-XBaoWE9RW8pPdPQNibZsW2zh8TW6gcarXp1FZPwT8Uop8ScSNldJEWf2k9l3HeTqdrEwsOsFcq74RiJECW34yA== -bindings@^1.5.0: +bindings@1, bindings@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== dependencies: file-uri-to-path "1.0.0" +bindings@~1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.2.1.tgz#14ad6113812d2d37d72e67b4cacb4bb726505f11" + integrity sha1-FK1hE4EtLTfXLme0ystLtyZQXxE= + bit-twiddle@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bit-twiddle/-/bit-twiddle-1.0.2.tgz#0c6c1fabe2b23d17173d9a61b7b7093eb9e1769e" @@ -7898,7 +7944,7 @@ buffer@^5.1.0, buffer@^5.2.0: base64-js "^1.0.2" ieee754 "^1.1.4" -builtin-modules@^1.0.0: +builtin-modules@^1.0.0, builtin-modules@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= @@ -8045,6 +8091,13 @@ cache-loader@^4.1.0: neo-async "^2.6.1" schema-utils "^2.0.0" +cacheable-lookup@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-2.0.0.tgz#33b1e56f17507f5cf9bb46075112d65473fb7713" + integrity sha512-s2piO6LvA7xnL1AR03wuEdSx3BZT3tIJpZ56/lcJwzO/6DTJZlTs7X3lrvPxk6d1PlDe6PrVe2TjlUIZNFglAQ== + dependencies: + keyv "^4.0.0" + cacheable-request@^2.1.1: version "2.1.4" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d" @@ -8058,6 +8111,19 @@ cacheable-request@^2.1.1: normalize-url "2.0.1" responselike "1.0.2" +cacheable-request@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.1.tgz#062031c2856232782ed694a257fa35da93942a58" + integrity sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^4.1.0" + responselike "^2.0.0" + cachedir@2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8" @@ -8886,7 +8952,7 @@ clone-regexp@^1.0.0: is-regexp "^1.0.0" is-supported-regexp-flag "^1.0.0" -clone-response@1.0.2: +clone-response@1.0.2, clone-response@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= @@ -9150,16 +9216,16 @@ commander@4.1.0: resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.0.tgz#545983a0603fe425bc672d66c9e3c89c42121a83" integrity sha512-NIQrwvv9V39FHgGFm36+U9SMQzbiHvU79k+iADraJTpmrFFfx7Ds0IvDoAdZsDrknlkRk14OYoWXb57uTh7/sw== +commander@^2.12.1, commander@^2.20.0, commander@^2.7.1: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + commander@^2.13.0, commander@^2.15.1, commander@^2.16.0, commander@^2.19.0: version "2.20.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== -commander@^2.20.0, commander@^2.7.1: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - commander@^2.8.1: version "2.18.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" @@ -10575,7 +10641,7 @@ debug-fabulous@1.X: memoizee "0.4.X" object-assign "4.X" -debug@2.6.9, debug@^2.0.0, debug@^2.1.0, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8, debug@^2.6.9: +debug@2, debug@2.6.9, debug@^2.0.0, debug@^2.1.0, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -10647,6 +10713,13 @@ decompress-response@^4.2.0: dependencies: mimic-response "^2.0.0" +decompress-response@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-5.0.0.tgz#7849396e80e3d1eba8cb2f75ef4930f76461cb0f" + integrity sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw== + dependencies: + mimic-response "^2.0.0" + decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1" @@ -10798,6 +10871,11 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" +defer-to-connect@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.0.tgz#83d6b199db041593ac84d781b5222308ccf4c2c1" + integrity sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg== + define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -13239,6 +13317,17 @@ fetch-mock@^7.3.9: path-to-regexp "^2.2.1" whatwg-url "^6.5.0" +ffi@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/ffi/-/ffi-2.3.0.tgz#fa1a7b3d85c0fa8c83d96947a64b5192bc47f858" + integrity sha512-vkPA9Hf9CVuQ5HeMZykYvrZF2QNJ/iKGLkyDkisBnoOOFeFXZQhUPxBARPBIZMJVulvBI2R+jgofW03gyPpJcQ== + dependencies: + bindings "~1.2.0" + debug "2" + nan "2" + ref "1" + ref-struct "1" + figgy-pudding@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" @@ -14716,6 +14805,27 @@ got@5.6.0: unzip-response "^1.0.0" url-parse-lax "^1.0.0" +got@^10.6.0: + version "10.6.0" + resolved "https://registry.yarnpkg.com/got/-/got-10.6.0.tgz#ac3876261a4d8e5fc4f81186f79955ce7b0501dc" + integrity sha512-3LIdJNTdCFbbJc+h/EH0V5lpNpbJ6Bfwykk21lcQvQsEcrzdi/ltCyQehFHLzJ/ka0UMH4Slg0hkYvAZN9qUDg== + dependencies: + "@sindresorhus/is" "^2.0.0" + "@szmarczak/http-timer" "^4.0.0" + "@types/cacheable-request" "^6.0.1" + cacheable-lookup "^2.0.0" + cacheable-request "^7.0.1" + decompress-response "^5.0.0" + duplexer3 "^0.1.4" + get-stream "^5.0.0" + lowercase-keys "^2.0.0" + mimic-response "^2.1.0" + p-cancelable "^2.0.0" + p-event "^4.0.0" + responselike "^2.0.0" + to-readable-stream "^2.0.0" + type-fest "^0.10.0" + got@^3.2.0: version "3.3.1" resolved "https://registry.yarnpkg.com/got/-/got-3.3.1.tgz#e5d0ed4af55fc3eef4d56007769d98192bcb2eca" @@ -15816,6 +15926,11 @@ http-cache-semantics@3.8.1: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + http-deceiver@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" @@ -16854,6 +16969,11 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.0.0.tgz#038c31b774709641bda678b1f06a4e3227c10b3e" integrity sha512-elzyIdM7iKoFHzcrndIqjYomImhxrFRnGP3galODoII4TB9gI7mZ+FnlLQmmjf27SxHS2gKEeyhX5/+YRS6H9g== +is-generator-function@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522" + integrity sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw== + is-glob@4.0.0, is-glob@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" @@ -18124,6 +18244,11 @@ json-buffer@3.0.0: resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-parse-better-errors@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.1.tgz#50183cd1b2d25275de069e9e71b467ac9eab973a" @@ -18359,7 +18484,7 @@ jsx-to-string@^1.4.0: json-stringify-pretty-compact "^1.0.1" react "^0.14.0" -jszip@^3.1.5: +jszip@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.2.2.tgz#b143816df7e106a9597a94c77493385adca5bd1d" integrity sha512-NmKajvAFQpbg3taXQXr/ccS2wcucR1AZ+NtyWp2Nq7HHVsXhcJFR8p0Baf32C2yVvBylFWVeKf+WI2AnvlPhpA== @@ -18535,6 +18660,13 @@ keyv@3.0.0: dependencies: json-buffer "3.0.0" +keyv@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.0.tgz#2d1dab694926b2d427e4c74804a10850be44c12f" + integrity sha512-U7ioE8AimvRVLfw4LffyOIRhL2xVgmE8T22L6i0BucSnBUyv4w+I7VN/zVZwRKHOI6ZRUcdMdWHQ8KSUvGpEog== + dependencies: + json-buffer "3.0.1" + killable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" @@ -19503,6 +19635,11 @@ lowercase-keys@^1.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + lowlight@~1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-1.9.1.tgz#ed7c3dffc36f8c1f263735c0fe0c907847c11250" @@ -20163,6 +20300,11 @@ mimic-response@^2.0.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.0.0.tgz#996a51c60adf12cb8a87d7fb8ef24c2f3d5ebb46" integrity sha512-8ilDoEapqA4uQ3TwS0jakGONKXVJqpy+RpM+3b7pLdOjghCrEiGp9SRkFbUHAmZW9vdnrENWHjaweIoTIJExSQ== +mimic-response@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43" + integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== + mimos@4.x.x: version "4.0.0" resolved "https://registry.yarnpkg.com/mimos/-/mimos-4.0.0.tgz#76e3d27128431cb6482fd15b20475719ad626a5a" @@ -20591,6 +20733,19 @@ move-concurrently@^1.0.1: rimraf "^2.5.4" run-queue "^1.0.3" +ms-chromium-edge-driver@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ms-chromium-edge-driver/-/ms-chromium-edge-driver-0.2.0.tgz#0e0c6fd9fd1d1d36db97b2b3d7e9d4ba4d2de456" + integrity sha512-RkDsBPnMLjRna7q4LlvtLb+CHPei9gZapnlxm3ayWKk3Ab6HmDsz/17xG2eyqkKX5UcKeo04YlLZ345tO7OolA== + dependencies: + extract-zip "^1.6.7" + got "^10.6.0" + lodash "4.17.15" + tslint "^6.1.0" + tslint-config-prettier "^1.18.0" + util "^0.12.2" + windows-registry "^0.1.5" + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -20703,7 +20858,7 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -nan@^2.12.1, nan@^2.13.2: +nan@2, nan@^2.12.1, nan@^2.13.2: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== @@ -21206,6 +21361,11 @@ normalize-url@^3.3.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== +normalize-url@^4.1.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" + integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== + now-and-later@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/now-and-later/-/now-and-later-2.0.0.tgz#bc61cbb456d79cb32207ce47ca05136ff2e7d6ee" @@ -21891,6 +22051,11 @@ p-cancelable@^0.4.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.4.1.tgz#35f363d67d52081c8d9585e37bcceb7e0bbcb2a0" integrity sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ== +p-cancelable@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.0.0.tgz#4a3740f5bdaf5ed5d7c3e34882c6fb5d6b266a6e" + integrity sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg== + p-defer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" @@ -21903,7 +22068,7 @@ p-each-series@^1.0.0: dependencies: p-reduce "^1.0.0" -p-event@^4.1.0: +p-event@^4.0.0, p-event@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/p-event/-/p-event-4.1.0.tgz#e92bb866d7e8e5b732293b1c8269d38e9982bf8e" integrity sha512-4vAd06GCsgflX4wHN1JqrMzBh/8QZ4j+rzp0cd2scXRwuBEv+QR3wrVA5aLhWDLw4y2WgDKvzWF3CCLmVM1UgA== @@ -24952,6 +25117,31 @@ redux@^4.0.5: loose-envify "^1.4.0" symbol-observable "^1.2.0" +ref-struct@1, ref-struct@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ref-struct/-/ref-struct-1.1.0.tgz#5d5ee65ad41cefc3a5c5feb40587261e479edc13" + integrity sha1-XV7mWtQc78Olxf60BYcmHkee3BM= + dependencies: + debug "2" + ref "1" + +ref-union@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ref-union/-/ref-union-1.0.1.tgz#3a2397f862f1e75171d687268f43b3f17729f120" + integrity sha1-OiOX+GLx51Fx1ocmj0Oz8Xcp8SA= + dependencies: + debug "2" + ref "1" + +ref@1, ref@^1.2.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ref/-/ref-1.3.5.tgz#0e33f080cdb94a3d95312b2b3b1fd0f82044ca0f" + integrity sha512-2cBCniTtxcGUjDpvFfVpw323a83/0RLSGJJY5l5lcomZWhYpU2cuLdsvYqMixvsdLJ9+sTdzEkju8J8ZHDM2nA== + dependencies: + bindings "1" + debug "2" + nan "2" + reflect.ownkeys@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460" @@ -25691,6 +25881,13 @@ responselike@1.0.2: dependencies: lowercase-keys "^1.0.0" +responselike@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" + integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== + dependencies: + lowercase-keys "^2.0.0" + restore-cursor@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" @@ -26249,15 +26446,14 @@ select@^1.1.2: resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0= -selenium-webdriver@^4.0.0-alpha.5: - version "4.0.0-alpha.5" - resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.0.0-alpha.5.tgz#e4683b3dbf827d70df09a7e43bf02ebad20fa7c1" - integrity sha512-hktl3DSrhzM59yLhWzDGHIX9o56DvA+cVK7Dw6FcJR6qQ4CGzkaHeXQPcdrslkWMTeq0Ci9AmCxq0EMOvm2Rkg== +selenium-webdriver@^4.0.0-alpha.7: + version "4.0.0-alpha.7" + resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.0.0-alpha.7.tgz#e3879d8457fd7ad8e4424094b7dc0540d99e6797" + integrity sha512-D4qnTsyTr91jT8f7MfN+OwY0IlU5+5FmlO5xlgRUV6hDEV8JyYx2NerdTEqDDkNq7RZDYc4VoPALk8l578RBHw== dependencies: - jszip "^3.1.5" - rimraf "^2.6.3" + jszip "^3.2.2" + rimraf "^2.7.1" tmp "0.0.30" - xml2js "^0.4.19" selfsigned@^1.10.7: version "1.10.7" @@ -28648,6 +28844,11 @@ to-object-path@^0.3.0: dependencies: kind-of "^3.0.2" +to-readable-stream@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-2.1.0.tgz#82880316121bea662cdc226adb30addb50cb06e8" + integrity sha512-o3Qa6DGg1CEXshSdvWNX2sN4QHqg03SPq7U6jPXRahlQdl5dK8oXjkU/2/sGrnOZKeGV1zLSO8qPwyKklPPE7w== + to-regex-range@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" @@ -28927,6 +29128,37 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.2, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== +tslint-config-prettier@^1.18.0: + version "1.18.0" + resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz#75f140bde947d35d8f0d238e0ebf809d64592c37" + integrity sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg== + +tslint@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-6.1.0.tgz#c6c611b8ba0eed1549bf5a59ba05a7732133d851" + integrity sha512-fXjYd/61vU6da04E505OZQGb2VCN2Mq3doeWcOIryuG+eqdmFUXTYVwdhnbEu2k46LNLgUYt9bI5icQze/j0bQ== + dependencies: + "@babel/code-frame" "^7.0.0" + builtin-modules "^1.1.1" + chalk "^2.3.0" + commander "^2.12.1" + diff "^4.0.1" + glob "^7.1.1" + js-yaml "^3.13.1" + minimatch "^3.0.4" + mkdirp "^0.5.1" + resolve "^1.3.2" + semver "^5.3.0" + tslib "^1.10.0" + tsutils "^2.29.0" + +tsutils@^2.29.0: + version "2.29.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" + integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== + dependencies: + tslib "^1.8.1" + tsutils@^3.17.1: version "3.17.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" @@ -29431,6 +29663,11 @@ type-detect@^1.0.0: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2" integrity sha1-diIXzAbbJY7EiQihKY6LlRIejqI= +type-fest@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.10.0.tgz#7f06b2b9fbfc581068d1341ffabd0349ceafc642" + integrity sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw== + type-fest@^0.3.0, type-fest@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1" @@ -30121,6 +30358,16 @@ util@^0.11.0: dependencies: inherits "2.0.3" +util@^0.12.2: + version "0.12.2" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.2.tgz#54adb634c9e7c748707af2bf5a8c7ab640cbba2b" + integrity sha512-XE+MkWQvglYa+IOfBt5UFG93EmncEMP23UqpgDvVZVFBPxwmkK10QRp6pgU4xICPnWRf/t0zPv4noYSUq9gqUQ== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + safe-buffer "^5.1.2" + utila@^0.4.0, utila@~0.4: version "0.4.0" resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" @@ -31277,6 +31524,17 @@ window-size@^0.2.0: resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" integrity sha1-tDFbtCFKPXBY6+7okuE/ok2YsHU= +windows-registry@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/windows-registry/-/windows-registry-0.1.5.tgz#92c25c960884b0d215e69395f52d8dfaa0ba4ad0" + integrity sha512-gMN3ets1fbdP+TApEbbX2TIfBK3MIH5+p9GMvIFS3CNLr7U0Khe5mRj/T5zvwo/pKdhJgDrCLYyaNSs7HYiBCw== + dependencies: + debug "^2.2.0" + ffi "^2.0.0" + ref "^1.2.0" + ref-struct "^1.0.2" + ref-union "^1.0.0" + windows-release@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.2.0.tgz#8122dad5afc303d833422380680a79cdfa91785f" @@ -31575,14 +31833,6 @@ xml-parse-from-string@^1.0.0: resolved "https://registry.yarnpkg.com/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz#a9029e929d3dbcded169f3c6e28238d95a5d5a28" integrity sha1-qQKekp09vN7RafPG4oI42VpdWig= -xml2js@^0.4.19, xml2js@^0.4.5: - version "0.4.19" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" - integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q== - dependencies: - sax ">=0.6.0" - xmlbuilder "~9.0.1" - xml2js@^0.4.22: version "0.4.22" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.22.tgz#4fa2d846ec803237de86f30aa9b5f70b6600de02" @@ -31592,6 +31842,14 @@ xml2js@^0.4.22: util.promisify "~1.0.0" xmlbuilder "~11.0.0" +xml2js@^0.4.5: + version "0.4.19" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" + integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q== + dependencies: + sax ">=0.6.0" + xmlbuilder "~9.0.1" + xml@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5"