diff --git a/.eslintrc.js b/.eslintrc.js index e66331594b4ae9..2ce6d279d93a9f 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: { @@ -552,29 +532,6 @@ module.exports = { }, }, - /** - * Graph overrides - */ - { - files: ['x-pack/legacy/plugins/graph/**/*.js'], - globals: { - angular: true, - $: true, - }, - rules: { - 'block-scoped-var': 'off', - camelcase: 'off', - eqeqeq: 'off', - 'guard-for-in': 'off', - 'new-cap': 'off', - 'no-loop-func': 'off', - 'no-redeclare': 'off', - 'no-shadow': 'off', - 'no-unused-vars': 'off', - 'one-var': 'off', - }, - }, - /** * ML overrides */ @@ -771,7 +728,7 @@ module.exports = { * Lens overrides */ { - files: ['x-pack/legacy/plugins/lens/**/*.ts', 'x-pack/legacy/plugins/lens/**/*.tsx'], + files: ['x-pack/legacy/plugins/lens/**/*.{ts,tsx}', 'x-pack/plugins/lens/**/*.{ts,tsx}'], rules: { '@typescript-eslint/no-explicit-any': 'error', }, @@ -885,8 +842,10 @@ module.exports = { * TSVB overrides */ { - files: ['src/legacy/core_plugins/metrics/**/*.js'], - excludedFiles: 'src/legacy/core_plugins/metrics/index.js', + files: [ + 'src/plugins/vis_type_timeseries/**/*.{js,ts,tsx}', + 'src/legacy/core_plugins/vis_type_timeseries/**/*.{js,ts,tsx}', + ], rules: { 'import/no-default-export': 'error', }, diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index da85fb986ae018..feaf47e45fd69c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -15,18 +15,20 @@ /src/legacy/core_plugins/metrics/ @elastic/kibana-app /src/legacy/core_plugins/vis_type_vislib/ @elastic/kibana-app /src/legacy/core_plugins/vis_type_xy/ @elastic/kibana-app -# Exclude tutorials folder for now because they are not owned by Kibana app and most will move out soon -/src/plugins/home/public @elastic/kibana-app -/src/plugins/home/server/*.ts @elastic/kibana-app -/src/plugins/home/server/services/ @elastic/kibana-app -# Exclude tutorial resources folder for now because they are not owned by Kibana app and most will move out soon -/src/legacy/core_plugins/kibana/public/home/*.ts @elastic/kibana-app -/src/legacy/core_plugins/kibana/public/home/*.scss @elastic/kibana-app -/src/legacy/core_plugins/kibana/public/home/np_ready/ @elastic/kibana-app /src/plugins/kibana_legacy/ @elastic/kibana-app /src/plugins/timelion/ @elastic/kibana-app -/src/plugins/dev_tools/ @elastic/kibana-app /src/plugins/dashboard/ @elastic/kibana-app +/src/plugins/discover/ @elastic/kibana-app + +# Core UI +# Exclude tutorials folder for now because they are not owned by Kibana app and most will move out soon +/src/plugins/home/public @elastic/kibana-core-ui +/src/plugins/home/server/*.ts @elastic/kibana-core-ui +/src/plugins/home/server/services/ @elastic/kibana-core-ui +# Exclude tutorial resources folder for now because they are not owned by Kibana app and most will move out soon +/src/legacy/core_plugins/kibana/public/home/*.ts @elastic/kibana-core-ui +/src/legacy/core_plugins/kibana/public/home/*.scss @elastic/kibana-core-ui +/src/legacy/core_plugins/kibana/public/home/np_ready/ @elastic/kibana-core-ui # App Architecture /examples/url_generators_examples/ @elastic/kibana-app-arch @@ -85,9 +87,8 @@ /x-pack/test/functional/apps/machine_learning/ @elastic/ml-ui /x-pack/test/functional/services/machine_learning/ @elastic/ml-ui /x-pack/test/functional/services/ml.ts @elastic/ml-ui -# ML team owns the transform plugin, ES team added here for visibility -# because the plugin lives in Kibana's Elasticsearch management section. -/x-pack/plugins/transform/ @elastic/ml-ui @elastic/es-ui +# ML team owns and maintains the transform plugin despite it living in the Elasticsearch management section. +/x-pack/plugins/transform/ @elastic/ml-ui /x-pack/test/functional/apps/transform/ @elastic/ml-ui /x-pack/test/functional/services/transform_ui/ @elastic/ml-ui /x-pack/test/functional/services/transform.ts @elastic/ml-ui @@ -175,6 +176,7 @@ **/*.scss @elastic/kibana-design # Elasticsearch UI +/src/plugins/dev_tools/ @elastic/es-ui /src/plugins/console/ @elastic/es-ui /src/plugins/es_ui_shared/ @elastic/es-ui /x-pack/legacy/plugins/cross_cluster_replication/ @elastic/es-ui diff --git a/.i18nrc.json b/.i18nrc.json index c293b3103a39c8..19d361aed93445 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/api/role-management/put.asciidoc b/docs/api/role-management/put.asciidoc index 59e6bc8d37eec7..993d75780c87c6 100644 --- a/docs/api/role-management/put.asciidoc +++ b/docs/api/role-management/put.asciidoc @@ -17,6 +17,7 @@ experimental[] Create a new {kib} role, or update the attributes of an existing To use the create or update role API, you must have the `manage_security` cluster privilege. +[role="child_attributes"] [[role-management-api-response-body]] ==== Request body @@ -29,8 +30,11 @@ To use the create or update role API, you must have the `manage_security` cluste {ref}/defining-roles.html[Defining roles]. `kibana`:: - (list) Objects that specify the <> for the role: - + (list) Objects that specify the <> for the role. ++ +.Properties of `kibana` +[%collapsible%open] +===== `base` ::: (Optional, list) A base privilege. When specified, the base must be `["all"]` or `["read"]`. When the `base` privilege is specified, you are unable to use the `feature` section. @@ -45,6 +49,7 @@ To use the create or update role API, you must have the `manage_security` cluste `spaces` ::: (list) The spaces to apply the privileges to. To grant access to all spaces, set to `["*"]`, or omit the value. +===== [[role-management-api-put-response-codes]] ==== Response code @@ -52,7 +57,7 @@ To use the create or update role API, you must have the `manage_security` cluste `204`:: Indicates a successful call. -===== Examples +==== Examples Grant access to various features in all spaces: diff --git a/docs/api/spaces-management/copy_saved_objects.asciidoc b/docs/api/spaces-management/copy_saved_objects.asciidoc index e23a137485b2d5..4822e7f624302d 100644 --- a/docs/api/spaces-management/copy_saved_objects.asciidoc +++ b/docs/api/spaces-management/copy_saved_objects.asciidoc @@ -26,6 +26,7 @@ You can request to overwrite any objects that already exist in the target space `space_id`:: (Optional, string) The ID of the space that contains the saved objects you want to copy. When `space_id` is unspecified in the URL, the default space is used. +[role="child_attributes"] [[spaces-api-copy-saved-objects-request-body]] ==== {api-request-body-title} @@ -34,10 +35,16 @@ You can request to overwrite any objects that already exist in the target space `objects`:: (Required, object array) The saved objects to copy. ++ +.Properties of `objects` +[%collapsible%open] +===== `type`::: (Required, string) The saved object type. + `id`::: (Required, string) The saved object ID. +===== `includeReferences`:: (Optional, boolean) When set to `true`, all saved objects related to the specified saved objects will also be copied into the target spaces. The default value is `false`. @@ -45,27 +52,43 @@ You can request to overwrite any objects that already exist in the target space `overwrite`:: (Optional, boolean) When set to `true`, all conflicts are automatically overidden. When a saved object with a matching `type` and `id` exists in the target space, that version is replaced with the version from the source space. The default value is `false`. - +[role="child_attributes"] [[spaces-api-copy-saved-objects-response-body]] ==== {api-response-body-title} ``:: (object) An object that describes the result of the copy operation for the space. Includes the dynamic keys in the response. ++ +.Properties of `` +[%collapsible%open] +===== `success`::: (boolean) The copy operation was successful. When set to `false`, some objects may have been copied. For additional information, refer to the `successCount` and `errors` properties. + `successCount`::: (number) The number of objects that successfully copied. + `errors`::: - (Optional, array) The errors that occurred during the copy operation. When errors are reported, the `success` flag is set to `false`.v + (Optional, array) The errors that occurred during the copy operation. When errors are reported, the `success` flag is set to `false`. ++ +.Properties of `errors` +[%collapsible%open] +====== `id`:::: (string) The saved object ID that failed to copy. `type`:::: (string) The type of saved object that failed to copy. `error`:::: (object) The error that caused the copy operation to fail. ++ +.Properties of `error` +[%collapsible%open] +======= `type`::::: (string) The type of error. For example, `unsupported_type`, `missing_references`, or `unknown`. Errors marked as `conflict` may be resolved by using the <>. - +======= +====== +===== [[spaces-api-copy-saved-objects-example]] ==== {api-examples-title} diff --git a/docs/api/spaces-management/resolve_copy_saved_objects_conflicts.asciidoc b/docs/api/spaces-management/resolve_copy_saved_objects_conflicts.asciidoc index 8e874bb9f94e5d..565d12513815bd 100644 --- a/docs/api/spaces-management/resolve_copy_saved_objects_conflicts.asciidoc +++ b/docs/api/spaces-management/resolve_copy_saved_objects_conflicts.asciidoc @@ -25,51 +25,89 @@ Execute the <>, w `space_id`:: (Optional, string) The ID of the space that contains the saved objects you want to copy. When `space_id` is unspecified in the URL, the default space is used. The `space_id` must be the same value used during the failed <> operation. +[role="child_attributes"] [[spaces-api-resolve-copy-saved-objects-conflicts-request-body]] ==== {api-request-body-title} `objects`:: (Required, object array) The saved objects to copy. The `objects` must be the same values used during the failed <> operation. ++ +.Properties of `objects` +[%collapsible%open] +===== `type`::: (Required, string) The saved object type. + `id`::: (Required, string) The saved object ID. +===== `includeReferences`:: (Optional, boolean) When set to `true`, all saved objects related to the specified saved objects are copied into the target spaces. The `includeReferences` must be the same values used during the failed <> operation. The default value is `false`. `retries`:: (Required, object) The retry operations to attempt. Object keys represent the target space IDs. ++ +.Properties of `retries` +[%collapsible%open] +===== ``::: (Required, array) The errors to resolve for the specified ``. ++ + +.Properties of `` +[%collapsible%open] +====== `type`:::: (Required, string) The saved object type. `id`:::: (Required, string) The saved object ID. `overwrite`:::: (Required, boolean) When set to `true`, the saved object from the source space (desigated by the <>) overwrites the conflicting object in the destination space. When set to `false`, this does nothing. +====== +===== - +[role="child_attributes"] [[spaces-api-resolve-copy-saved-objects-conflicts-response-body]] ==== {api-response-body-title} ``:: (object) An object that describes the result of the copy operation for the space. Includes the dynamic keys in the response. ++ +.Properties of `` +[%collapsible%open] +===== `success`::: (boolean) The copy operation was successful. When set to `false`, some objects may have been copied. For additional information, refer to the `successCount` and `errors` properties. + `successCount`::: (number) The number of objects that successfully copied. + `errors`::: (Optional, array) The errors that occurred during the copy operation. When errors are reported, the `success` flag is set to `false`. ++ + +.Properties of `errors` +[%collapsible%open] +====== `id`:::: (string) The saved object ID that failed to copy. + `type`:::: (string) The type of saved object that failed to copy. + `error`:::: (object) The error that caused the copy operation to fail. - `type`::::: - (string) The type of error. For example, `unsupported_type`, `missing_references`, or `unknown`. ++ +.Properties of `error` +[%collapsible%open] +======= + `type`:::: + (string) The type of error. For example, `unsupported_type`, `missing_references`, or `unknown`. +======= +====== +===== [[spaces-api-resolve-copy-saved-objects-conflicts-example]] ==== {api-examples-title} diff --git a/docs/apm/api.asciidoc b/docs/apm/api.asciidoc index b520cc46bef8d3..a8f4f4bf0baaab 100644 --- a/docs/apm/api.asciidoc +++ b/docs/apm/api.asciidoc @@ -38,17 +38,22 @@ The following Agent configuration APIs are available: `PUT /api/apm/settings/agent-configuration` +[role="child_attributes"] [[apm-update-config-req-body]] ===== Request body `service`:: (required, object) Service identifying the configuration to create or update. - ++ +.Properties of `service` +[%collapsible%open] +====== `name` ::: (required, string) Name of service `environment` ::: (optional, string) Environment of service +====== `settings`:: (required) Key/value object with settings and their corresponding value. @@ -90,16 +95,21 @@ PUT /api/apm/settings/agent-configuration `DELETE /api/apm/settings/agent-configuration` +[role="child_attributes"] [[apm-delete-config-req-body]] ===== Request body `service`:: (required, object) Service identifying the configuration to delete - ++ +.Properties of `service` +[%collapsible%open] +====== `name` ::: (required, string) Name of service `environment` ::: (optional, string) Environment of service +====== [[apm-delete-config-example]] @@ -201,17 +211,22 @@ GET /api/apm/settings/agent-configuration `POST /api/apm/settings/agent-configuration/search` +[role="child_attributes"] [[apm-search-config-req-body]] ===== Request body `service`:: (required, object) Service identifying the configuration. - ++ +.Properties of `service` +[%collapsible%open] +====== `name` ::: (required, string) Name of service `environment` ::: (optional, string) Environment of service +====== `etag`:: (required) etag is sent by the agent to indicate the etag of the last successfully applied configuration. If the etag matches an existing configuration its `applied_by_agent` property will be set to `true`. Every time a configuration is edited `applied_by_agent` is reset to `false`. diff --git a/docs/apm/images/service-maps-java.png b/docs/apm/images/service-maps-java.png new file mode 100644 index 00000000000000..e1a42f4c76e12f 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 00000000000000..454ae9bb720fbd 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 00000000000000..e0d84f33b4dcb9 --- /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/transactions.asciidoc b/docs/apm/transactions.asciidoc index 536ab2ec29c80f..5c92afa55109d5 100644 --- a/docs/apm/transactions.asciidoc +++ b/docs/apm/transactions.asciidoc @@ -103,9 +103,7 @@ The number of requests per bucket is displayed when hovering over the graph, and [role="screenshot"] image::apm/images/apm-transaction-duration-dist.png[Example view of transactions duration distribution graph] -Most of the requests fall into buckets on the left side of the graph, -with a long tail of smaller buckets to the right. -This is a typical distribution, and indicates most of our requests were served quickly - awesome! +This graph shows a typical distribution, and indicates most of our requests were served quickly - awesome! It's the requests on the right, the ones taking longer than average, that we probably want to focus on. When you select one of these buckets, you're presented with up to ten trace samples. diff --git a/docs/apm/using-the-apm-ui.asciidoc b/docs/apm/using-the-apm-ui.asciidoc index 1361dc046e3b1f..b1b7ed73079866 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/canvas/canvas-elements.asciidoc b/docs/canvas/canvas-elements.asciidoc index 163579d5763b2e..a25460a20eb50c 100644 --- a/docs/canvas/canvas-elements.asciidoc +++ b/docs/canvas/canvas-elements.asciidoc @@ -138,7 +138,9 @@ To apply CSS overrides: . Next to *Element style*, click *+*, then select *CSS*. -. Enter the *CSS*. For example, to center the Markdown element, enter: +. Enter the *CSS*. ++ +For example, to center the Markdown element, enter: + [source,text] -------------------------------------------------- diff --git a/docs/canvas/canvas-present-workpad.asciidoc b/docs/canvas/canvas-present-workpad.asciidoc index 486686cd857b5e..9cd4ecc9519e15 100644 --- a/docs/canvas/canvas-present-workpad.asciidoc +++ b/docs/canvas/canvas-present-workpad.asciidoc @@ -8,7 +8,7 @@ When you are ready to present your workpad, use and enable the presentation opti [[view-fullscreen-mode]] ==== View your workpad in fullscreen mode -In the upper left corner, click the *Enter fullscreen mode* icon. +Click the *Enter fullscreen mode* icon. [role="screenshot"] image::images/canvas-fullscreen.png[Fullscreen mode] @@ -19,7 +19,7 @@ image::images/canvas-fullscreen.png[Fullscreen mode] Automatically cycle through your workpads pages in fullscreen mode. -. In the upper left corner, click the *Control settings* icon. +. Click the *Control settings* icon. . Under *Change cycling interval*, select the interval you want to use. + diff --git a/docs/canvas/canvas-share-workpad.asciidoc b/docs/canvas/canvas-share-workpad.asciidoc index dbba12865b8ca7..ee29926914ad61 100644 --- a/docs/canvas/canvas-share-workpad.asciidoc +++ b/docs/canvas/canvas-share-workpad.asciidoc @@ -10,7 +10,7 @@ When you've finished your workpad, you can share it outside of {kib}. Create a JSON file of your workpad that you can export outside of {kib}. -. From your workpad, click the *Share workpad* icon in the upper left corner. +. From your workpad, click the *Share workpad* icon. . Select *Download as JSON*. + @@ -27,7 +27,7 @@ If you have a license that supports the {report-features}, you can create a PDF For more information, refer to <>. -. From your workpad, click the *Share workpad* icon in the upper left corner, then select *PDF reports*. +. From your workpad, click the *Share workpad* icon, then select *PDF reports*. . Click *Generate PDF*. + @@ -42,7 +42,7 @@ If you have a license that supports the {report-features}, you can create a POST For more information, refer to <>. -. From your workpad, click the *Share workpad* icon in the upper left corner, then select *PDF reports*. +. From your workpad, click the *Share workpad* icon, then select *PDF reports*. . Click *Copy POST URL*. + @@ -55,7 +55,7 @@ image::images/canvas-create-URL.gif[Create POST URL] beta[] Canvas allows you to create _shareables_, which are workpads that you download and securely share on any website. To customize the behavior of the workpad on your website, you can choose to autoplay the pages or hide the workpad toolbar. -. From your workpad, click the *Share this workpad* icon in the upper left corner, then select *Share on a website*. +. From your workpad, click the *Share this workpad* icon, then select *Share on a website*. . On the *Share on a website* pane, follow the instructions. diff --git a/docs/dev-tools/searchprofiler/getting-started.asciidoc b/docs/dev-tools/searchprofiler/getting-started.asciidoc index 2360e4c28ff15a..4a87d4b84b7836 100644 --- a/docs/dev-tools/searchprofiler/getting-started.asciidoc +++ b/docs/dev-tools/searchprofiler/getting-started.asciidoc @@ -3,10 +3,10 @@ === Getting Started The {searchprofiler} is automatically enabled in {kib}. Go to *Dev Tools > Search Profiler* -to get started. +to get started. {searchprofiler} displays the names of the indices searched, the shards in each index, -and how long it took for the query to complete. To try it out, replace the default `match_all` query +and how long it took for the query to complete. To try it out, replace the default `match_all` query with the query you want to profile and click *Profile*. The following example shows the results of profiling the `match_all` query. @@ -29,8 +29,8 @@ While the Cumulative Time metric is useful for comparing the performance of your indices and shards, it doesn't necessarily represent the actual physical query times. ==== -You can select the name of the shard and then click *View details* to see more profiling information, -including details about the query component(s) that ran on the shard, as well as the timing +You can select the name of the shard and then click *View details* to see more profiling information, +including details about the query component(s) that ran on the shard, as well as the timing breakdown of low-level Lucene methods. For more information, see {ref}/search-profile.html#profiling-queries[Profiling queries]. [float] @@ -40,10 +40,10 @@ By default, all queries executed by the {searchprofiler} are sent to `GET /_search`. It searches across your entire cluster (all indices, all types). If you need to query a specific index or type (or several), you can use the Index -and Type filters at the top left. +and Type filters. In the following example, the query is executed against the indices `test` and `kibana_1` and the type `my_type`. This is equivalent making a request to `GET /test,kibana_1/my_type/_search`. [role="screenshot"] -image::dev-tools/searchprofiler/images/filter.png["Filtering by index and type"] \ No newline at end of file +image::dev-tools/searchprofiler/images/filter.png["Filtering by index and type"] diff --git a/docs/developer/plugin/development-plugin-feature-registration.asciidoc b/docs/developer/plugin/development-plugin-feature-registration.asciidoc index ca61e5309ce85c..4702204196bf21 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 29fdc37a811767..c10b460da8b4f7 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 00000000000000..f5ea627a9f008a --- /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 00000000000000..b41e7020c38e95 --- /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 00000000000000..3fde86a18c58bd --- /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 00000000000000..d554c6f70d7208 --- /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 00000000000000..f8a45fe9a5a9ce --- /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 00000000000000..2398410fa4b840 --- /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 00000000000000..7374ccd9e7fa87 --- /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 793684c1b37961..accab9bf0cb360 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 00000000000000..8e7298d28801c9 --- /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 00000000000000..82a4800a3b4b63 --- /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 00000000000000..347f2d3474b114 --- /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 00000000000000..6fcfacc3bc9085 --- /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 00000000000000..415a7825ee2bfe --- /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 00000000000000..6c017e9fc800ce --- /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 00000000000000..3a0b23d18632f1 --- /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 00000000000000..6a29623b2f122d --- /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 00000000000000..fa369aa0bdfbb4 --- /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 00000000000000..5ef8c1251a6024 --- /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 00000000000000..551c10c9bff820 --- /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 00000000000000..d35fc951c57ffb --- /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 00000000000000..a48994daa5a4e4 --- /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 00000000000000..db90afd6f74a65 --- /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 00000000000000..5f995ff5e13e3b --- /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 00000000000000..a66cec78c736b8 --- /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 00000000000000..6662e68b44d364 --- /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 00000000000000..0551a217520ad7 --- /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.search.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md index afb6ea88f9fad7..78ac05b9fd3861 100644 --- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md +++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.search.md @@ -19,7 +19,7 @@ search: { intervalOptions: ({ display: string; val: string; - enabled(agg: import("./search/aggs/buckets/_bucket_agg_type").IBucketAggConfig): boolean | "" | undefined; + enabled(agg: import("./search/aggs/buckets/bucket_agg_type").IBucketAggConfig): boolean | "" | undefined; } | { display: string; val: string; 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 259d725b3bf0d9..e756eb9b729050 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/discover/document-data.asciidoc b/docs/discover/document-data.asciidoc index 6e9218d66c1154..477c2ec90e95cf 100644 --- a/docs/discover/document-data.asciidoc +++ b/docs/discover/document-data.asciidoc @@ -26,7 +26,7 @@ and click image:images/sort-icon.png[]. The first click sorts by ascending order, the second click sorts by descending order, and the third click removes the field from the sorted fields. -Move a field column:: Hover over the column header and click the move left (<<) or move right icon (>>). +Move a field column:: Hover over the column header and click the (<<) or (>>) icons. Remove a field column :: Hover over the list of *Specified fields* and then click *remove*. Or, use the (x) control in the column header. diff --git a/docs/getting-started/tutorial-visualizing.asciidoc b/docs/getting-started/tutorial-visualizing.asciidoc index a16343aa4850a2..acd4d6d908fd45 100644 --- a/docs/getting-started/tutorial-visualizing.asciidoc +++ b/docs/getting-started/tutorial-visualizing.asciidoc @@ -180,5 +180,5 @@ The map now looks like this: image::images/tutorial-visualize-map-2.png[] . Navigate the map by clicking and dragging. Use the controls -on the left to zoom the map and set filters. +to zoom the map and set filters. . *Save* this map with the name `Map Example`. diff --git a/docs/infrastructure/view-metrics.asciidoc b/docs/infrastructure/view-metrics.asciidoc index bbb981acc3ad6c..1bd64dde76ee13 100644 --- a/docs/infrastructure/view-metrics.asciidoc +++ b/docs/infrastructure/view-metrics.asciidoc @@ -30,11 +30,3 @@ For complete control over the start and end times, click the start time or end t === Refresh the metrics You can click *Refresh* to manually refresh the metrics. - -[float] -[[infra-view-go-to-chart]] -=== Go to a specific chart - -The charts available for this component are shown in a list on the left of the page. Click a chart in the list to quickly go to that chart. - - diff --git a/docs/management/managing-licenses.asciidoc b/docs/management/managing-licenses.asciidoc index 72accdb5fe2aab..a7ed4e942f3f60 100644 --- a/docs/management/managing-licenses.asciidoc +++ b/docs/management/managing-licenses.asciidoc @@ -15,8 +15,7 @@ already activated a trial for 6.0, you cannot start a new trial until 7.0. You can, however, contact `info@elastic.co` to request an extended trial license. -When you activate a new license level, new features appear in the left sidebar -of the *Management* page. +When you activate a new license level, new features appear in *Management*. [role="screenshot"] image::images/management-license.png[] diff --git a/docs/spaces/index.asciidoc b/docs/spaces/index.asciidoc index fb5ef670692dc8..990af3a018b1fb 100644 --- a/docs/spaces/index.asciidoc +++ b/docs/spaces/index.asciidoc @@ -9,10 +9,10 @@ the dashboards and saved objects that belong to that space. {kib} creates a default space for you. After you create your own spaces, you're asked to choose a space when you log in to Kibana. You can change your -current space at any time by using the menu in the upper left. +current space at any time by using the menu. [role="screenshot"] -image::spaces/images/change-space.png["Change current space"] +image::spaces/images/change-space.png["Change current space menu"] Kibana supports spaces in several ways. You can: diff --git a/docs/user/dashboard.asciidoc b/docs/user/dashboard.asciidoc index 490edb9d263381..a17e46c5b3542e 100644 --- a/docs/user/dashboard.asciidoc +++ b/docs/user/dashboard.asciidoc @@ -93,7 +93,7 @@ In *Edit* mode, you can move, resize, customize, and delete panels to suit your * To resize a panel, click the resize control on the lower right and drag to the new dimensions. -* To toggle the use of margins and panel titles, use the *Options* menu in the upper left. +* To toggle the use of margins and panel titles, use the *Options* menu. * To delete a panel, open the panel menu and select *Delete from dashboard.* Deleting a panel from a dashboard does *not* delete the saved visualization or search. diff --git a/docs/user/discover.asciidoc b/docs/user/discover.asciidoc index 7de7d73bf16642..4222ba40debb75 100644 --- a/docs/user/discover.asciidoc +++ b/docs/user/discover.asciidoc @@ -24,7 +24,7 @@ image::images/Discover-Start.png[Discover] === Set up your index pattern The first thing to do in *Discover* is to select an <>, which -defines the data you want to explore and visualize. The current index pattern is in the upper left. +defines the data you want to explore and visualize. If you haven't yet created an index pattern, you can add a <>, which has a pre-built index pattern. @@ -69,7 +69,7 @@ image::images/filter-field.png[height=317] The sortable documents table lists the documents that match your search. By default, the table includes columns for the time field and the document `_source`. -To zero in on a specific field, click *add* next to the field name in the left sidebar. +To zero in on a specific field, click *add* next to the field name. For example, if you add the `currency`, `customer_last_name`, and `day_of_week` fields, the document table includes columns for those three fields. diff --git a/docs/user/security/authorization/kibana-privileges.asciidoc b/docs/user/security/authorization/kibana-privileges.asciidoc index 6a0b7cefab018b..21fcb9bdccb878 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 e7d000d4554ad3..26fbbf20b39ad6 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/docs/visualize/lens.asciidoc b/docs/visualize/lens.asciidoc index e3f61565453b5e..35570ea7ca1dce 100644 --- a/docs/visualize/lens.asciidoc +++ b/docs/visualize/lens.asciidoc @@ -32,7 +32,7 @@ Lens supports the following aggregations: [[drag-drop]] === Drag and drop -The data panel in the left column shows the data fields for the selected time period. When +The panel shows the data fields for the selected time period. When you drag a field from the data panel, Lens highlights where you can drop that field. The first time you drag a data field, you'll see two places highlighted in green: @@ -57,7 +57,7 @@ Lens shows you fields based on the <> you have d {kib}, and the current time range. When you change the index pattern or time filter, the list of fields are updated. -To narrow the list of fields you see in the left panel, you can: +To narrow the list of fields, you can: * Enter the field name in *Search field names*. @@ -100,11 +100,7 @@ still allows you to make the change. Lens allows some customizations of the data for each visualization. -. Change the index pattern. - -.. In the left column, click the index pattern name. - -.. Select the new index pattern. +. Click the index pattern name, then select the new index pattern. + If there is a match, Lens displays the new data. All fields that do not match the index pattern are removed. @@ -147,7 +143,7 @@ Drag and drop your data onto the visualization builder pane. . On the *New Visualization* window, click *Lens*. -. In the left column, select the *kibana_sample_data_ecommerce* index. +. Select the *kibana_sample_data_ecommerce* index. . Click image:images/time-filter-calendar.png[], then click *Last 7 days*. The list of data fields are updated. diff --git a/examples/embeddable_examples/public/list_container/embeddable_list_item.tsx b/examples/embeddable_examples/public/list_container/embeddable_list_item.tsx deleted file mode 100644 index 2c80cef8a63648..00000000000000 --- a/examples/embeddable_examples/public/list_container/embeddable_list_item.tsx +++ /dev/null @@ -1,64 +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 React from 'react'; -import { EuiPanel, EuiLoadingSpinner, EuiFlexItem } from '@elastic/eui'; -import { IEmbeddable } from '../../../../src/plugins/embeddable/public'; - -interface Props { - embeddable: IEmbeddable; -} - -export class EmbeddableListItem extends React.Component { - private embeddableRoot: React.RefObject; - private rendered = false; - - constructor(props: Props) { - super(props); - this.embeddableRoot = React.createRef(); - } - - public componentDidMount() { - if (this.embeddableRoot.current && this.props.embeddable) { - this.props.embeddable.render(this.embeddableRoot.current); - this.rendered = true; - } - } - - public componentDidUpdate() { - if (this.embeddableRoot.current && this.props.embeddable && !this.rendered) { - this.props.embeddable.render(this.embeddableRoot.current); - this.rendered = true; - } - } - - public render() { - return ( - - - {this.props.embeddable ? ( -
- ) : ( - - )} - - - ); - } -} diff --git a/examples/embeddable_examples/public/list_container/list_container.tsx b/examples/embeddable_examples/public/list_container/list_container.tsx index bbbd0d6e323046..9e7bec7a1c951e 100644 --- a/examples/embeddable_examples/public/list_container/list_container.tsx +++ b/examples/embeddable_examples/public/list_container/list_container.tsx @@ -31,16 +31,14 @@ export class ListContainer extends Container<{}, ContainerInput> { public readonly type = LIST_CONTAINER; private node?: HTMLElement; - constructor( - input: ContainerInput, - getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory'] - ) { - super(input, { embeddableLoaded: {} }, getEmbeddableFactory); + constructor(input: ContainerInput, private embeddableServices: EmbeddableStart) { + super(input, { embeddableLoaded: {} }, embeddableServices.getEmbeddableFactory); } - // This container has no input itself. - getInheritedInput(id: string) { - return {}; + getInheritedInput() { + return { + viewMode: this.input.viewMode, + }; } public render(node: HTMLElement) { @@ -48,7 +46,10 @@ export class ListContainer extends Container<{}, ContainerInput> { if (this.node) { ReactDOM.unmountComponentAtNode(this.node); } - ReactDOM.render(, node); + ReactDOM.render( + , + node + ); } public destroy() { diff --git a/examples/embeddable_examples/public/list_container/list_container_component.tsx b/examples/embeddable_examples/public/list_container/list_container_component.tsx index f6e04933ee8971..da27889a276036 100644 --- a/examples/embeddable_examples/public/list_container/list_container_component.tsx +++ b/examples/embeddable_examples/public/list_container/list_container_component.tsx @@ -24,30 +24,35 @@ import { withEmbeddableSubscription, ContainerInput, ContainerOutput, + EmbeddableStart, } from '../../../../src/plugins/embeddable/public'; -import { EmbeddableListItem } from './embeddable_list_item'; interface Props { embeddable: IContainer; input: ContainerInput; output: ContainerOutput; + embeddableServices: EmbeddableStart; } -function renderList(embeddable: IContainer, panels: ContainerInput['panels']) { +function renderList( + embeddable: IContainer, + panels: ContainerInput['panels'], + embeddableServices: EmbeddableStart +) { let number = 0; const list = Object.values(panels).map(panel => { const child = embeddable.getChild(panel.explicitInput.id); number++; return ( - +

{number}

- +
@@ -56,12 +61,12 @@ function renderList(embeddable: IContainer, panels: ContainerInput['panels']) { return list; } -export function ListContainerComponentInner(props: Props) { +export function ListContainerComponentInner({ embeddable, input, embeddableServices }: Props) { return (
-

{props.embeddable.getTitle()}

+

{embeddable.getTitle()}

- {renderList(props.embeddable, props.input.panels)} + {renderList(embeddable, input.panels, embeddableServices)}
); } @@ -71,4 +76,9 @@ export function ListContainerComponentInner(props: Props) { // anything on input or output state changes. If you don't want that to happen (for example // if you expect something on input or output state to change frequently that your react // component does not care about, then you should probably hook this up manually). -export const ListContainerComponent = withEmbeddableSubscription(ListContainerComponentInner); +export const ListContainerComponent = withEmbeddableSubscription< + ContainerInput, + ContainerOutput, + IContainer, + { embeddableServices: EmbeddableStart } +>(ListContainerComponentInner); diff --git a/examples/embeddable_examples/public/list_container/list_container_factory.ts b/examples/embeddable_examples/public/list_container/list_container_factory.ts index 1fde254110c624..02a024b95349fa 100644 --- a/examples/embeddable_examples/public/list_container/list_container_factory.ts +++ b/examples/embeddable_examples/public/list_container/list_container_factory.ts @@ -26,7 +26,7 @@ import { import { LIST_CONTAINER, ListContainer } from './list_container'; interface StartServices { - getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']; + embeddableServices: EmbeddableStart; } export class ListContainerFactory implements EmbeddableFactoryDefinition { @@ -40,8 +40,8 @@ export class ListContainerFactory implements EmbeddableFactoryDefinition { } public create = async (initialInput: ContainerInput) => { - const { getEmbeddableFactory } = await this.getStartServices(); - return new ListContainer(initialInput, getEmbeddableFactory); + const { embeddableServices } = await this.getStartServices(); + return new ListContainer(initialInput, embeddableServices); }; public getDisplayName() { diff --git a/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_component.tsx b/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_component.tsx index e33dfab0eaf4a2..b2882c97ef501e 100644 --- a/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_component.tsx +++ b/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_component.tsx @@ -54,7 +54,7 @@ function wrapSearchTerms(task: string, search?: string) { ); } -function renderTasks(tasks: MultiTaskTodoOutput['tasks'], search?: string) { +function renderTasks(tasks: MultiTaskTodoInput['tasks'], search?: string) { return tasks.map(task => ( + {icon ? : } - +

{wrapSearchTerms(title, search)}

@@ -89,6 +88,8 @@ export function MultiTaskTodoEmbeddableComponentInner({ ); } -export const MultiTaskTodoEmbeddableComponent = withEmbeddableSubscription( - MultiTaskTodoEmbeddableComponentInner -); +export const MultiTaskTodoEmbeddableComponent = withEmbeddableSubscription< + MultiTaskTodoInput, + MultiTaskTodoOutput, + MultiTaskTodoEmbeddable +>(MultiTaskTodoEmbeddableComponentInner); diff --git a/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable.tsx b/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable.tsx index a2197c9c06fe94..a9e58c5538107b 100644 --- a/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable.tsx +++ b/examples/embeddable_examples/public/multi_task_todo/multi_task_todo_embeddable.tsx @@ -36,30 +36,27 @@ export interface MultiTaskTodoInput extends EmbeddableInput { title: string; } -// This embeddable has output! It's the tasks list that is filtered. -// Output state is something only the embeddable itself can update. It -// can be something completely internal, or it can be state that is +// This embeddable has output! Output state is something only the embeddable itself +// can update. It can be something completely internal, or it can be state that is // derived from input state and updates when input does. export interface MultiTaskTodoOutput extends EmbeddableOutput { - tasks: string[]; + hasMatch: boolean; } -function getFilteredTasks(tasks: string[], search?: string) { - const filteredTasks: string[] = []; - if (search === undefined) return tasks; +function getHasMatch(tasks: string[], title?: string, search?: string) { + if (search === undefined || search === '') return false; - tasks.forEach(task => { - if (task.match(search)) { - filteredTasks.push(task); - } - }); + if (title && title.match(search)) return true; + + const match = tasks.find(task => task.match(search)); + if (match) return true; - return filteredTasks; + return false; } function getOutput(input: MultiTaskTodoInput) { - const tasks = getFilteredTasks(input.tasks, input.search); - return { tasks, hasMatch: tasks.length > 0 || (input.search && input.title.match(input.search)) }; + const hasMatch = getHasMatch(input.tasks, input.title, input.search); + return { hasMatch }; } export class MultiTaskTodoEmbeddable extends Embeddable { diff --git a/examples/embeddable_examples/public/plugin.ts b/examples/embeddable_examples/public/plugin.ts index 5c202d96ceb1a2..31a3037332dda9 100644 --- a/examples/embeddable_examples/public/plugin.ts +++ b/examples/embeddable_examples/public/plugin.ts @@ -53,20 +53,17 @@ export class EmbeddableExamplesPlugin new MultiTaskTodoEmbeddableFactory() ); - // These are registered in the start method because `getEmbeddableFactory ` - // is only available in start. We could reconsider this I think and make it - // available in both. deps.embeddable.registerEmbeddableFactory( SEARCHABLE_LIST_CONTAINER, new SearchableListContainerFactory(async () => ({ - getEmbeddableFactory: (await core.getStartServices())[1].embeddable.getEmbeddableFactory, + embeddableServices: (await core.getStartServices())[1].embeddable, })) ); deps.embeddable.registerEmbeddableFactory( LIST_CONTAINER, new ListContainerFactory(async () => ({ - getEmbeddableFactory: (await core.getStartServices())[1].embeddable.getEmbeddableFactory, + embeddableServices: (await core.getStartServices())[1].embeddable, })) ); diff --git a/examples/embeddable_examples/public/searchable_list_container/searchable_list_container.tsx b/examples/embeddable_examples/public/searchable_list_container/searchable_list_container.tsx index 06462937c768d7..f6efb0b722c4c4 100644 --- a/examples/embeddable_examples/public/searchable_list_container/searchable_list_container.tsx +++ b/examples/embeddable_examples/public/searchable_list_container/searchable_list_container.tsx @@ -40,11 +40,8 @@ export class SearchableListContainer extends Container, node); + ReactDOM.render( + , + node + ); } public destroy() { diff --git a/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_component.tsx b/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_component.tsx index b79f86e2a0192d..49dbce74788bfa 100644 --- a/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_component.tsx +++ b/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_component.tsx @@ -34,14 +34,15 @@ import { withEmbeddableSubscription, ContainerOutput, EmbeddableOutput, + EmbeddableStart, } from '../../../../src/plugins/embeddable/public'; -import { EmbeddableListItem } from '../list_container/embeddable_list_item'; import { SearchableListContainer, SearchableContainerInput } from './searchable_list_container'; interface Props { embeddable: SearchableListContainer; input: SearchableContainerInput; output: ContainerOutput; + embeddableServices: EmbeddableStart; } interface State { @@ -111,13 +112,27 @@ export class SearchableListContainerComponentInner extends Component { + const { input, embeddable } = this.props; + const checked: { [key: string]: boolean } = {}; + Object.values(input.panels).map(panel => { + const child = embeddable.getChild(panel.explicitInput.id); + const output = child.getOutput(); + if (hasHasMatchOutput(output) && output.hasMatch) { + checked[panel.explicitInput.id] = true; + } + }); + this.setState({ checked }); + }; + private toggleCheck = (isChecked: boolean, id: string) => { this.setState(prevState => ({ checked: { ...prevState.checked, [id]: isChecked } })); }; public renderControls() { + const { input } = this.props; return ( - + this.deleteChecked()}> @@ -125,6 +140,17 @@ export class SearchableListContainerComponentInner extends Component + + + this.checkMatching()} + > + Check matching + + + -

{embeddable.getTitle()}

- - {this.renderControls()} - - {this.renderList()} -
+ + +

{embeddable.getTitle()}

+ + {this.renderControls()} + + {this.renderList()} +
+
); } private renderList() { + const { embeddableServices, input, embeddable } = this.props; let id = 0; - const list = Object.values(this.props.input.panels).map(panel => { - const embeddable = this.props.embeddable.getChild(panel.explicitInput.id); - if (this.props.input.search && !this.state.hasMatch[panel.explicitInput.id]) return; + const list = Object.values(input.panels).map(panel => { + const childEmbeddable = embeddable.getChild(panel.explicitInput.id); id++; - return embeddable ? ( - - + return childEmbeddable ? ( + + this.toggleCheck(e.target.checked, embeddable.id)} + data-test-subj={`todoCheckBox-${childEmbeddable.id}`} + disabled={!childEmbeddable} + id={childEmbeddable ? childEmbeddable.id : ''} + checked={this.state.checked[childEmbeddable.id]} + onChange={e => this.toggleCheck(e.target.checked, childEmbeddable.id)} /> - + @@ -183,6 +211,9 @@ export class SearchableListContainerComponentInner extends Component(SearchableListContainerComponentInner); diff --git a/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_factory.ts b/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_factory.ts index 382bb65e769ef2..34ea43c29462a8 100644 --- a/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_factory.ts +++ b/examples/embeddable_examples/public/searchable_list_container/searchable_list_container_factory.ts @@ -29,7 +29,7 @@ import { } from './searchable_list_container'; interface StartServices { - getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']; + embeddableServices: EmbeddableStart; } export class SearchableListContainerFactory implements EmbeddableFactoryDefinition { @@ -43,8 +43,8 @@ export class SearchableListContainerFactory implements EmbeddableFactoryDefiniti } public create = async (initialInput: SearchableContainerInput) => { - const { getEmbeddableFactory } = await this.getStartServices(); - return new SearchableListContainer(initialInput, getEmbeddableFactory); + const { embeddableServices } = await this.getStartServices(); + return new SearchableListContainer(initialInput, embeddableServices); }; public getDisplayName() { diff --git a/examples/embeddable_examples/public/todo/todo_component.tsx b/examples/embeddable_examples/public/todo/todo_component.tsx index fbebfc98627b58..a4593bea3cc5e5 100644 --- a/examples/embeddable_examples/public/todo/todo_component.tsx +++ b/examples/embeddable_examples/public/todo/todo_component.tsx @@ -51,12 +51,12 @@ function wrapSearchTerms(task: string, search?: string) { export function TodoEmbeddableComponentInner({ input: { icon, title, task, search } }: Props) { return ( - + {icon ? : } - +

{wrapSearchTerms(title || '', search)}

@@ -71,4 +71,8 @@ export function TodoEmbeddableComponentInner({ input: { icon, title, task, searc ); } -export const TodoEmbeddableComponent = withEmbeddableSubscription(TodoEmbeddableComponentInner); +export const TodoEmbeddableComponent = withEmbeddableSubscription< + TodoInput, + EmbeddableOutput, + TodoEmbeddable +>(TodoEmbeddableComponentInner); diff --git a/examples/embeddable_explorer/public/app.tsx b/examples/embeddable_explorer/public/app.tsx index 9c8568454855db..e18012b4b3d806 100644 --- a/examples/embeddable_explorer/public/app.tsx +++ b/examples/embeddable_explorer/public/app.tsx @@ -117,18 +117,7 @@ const EmbeddableExplorerApp = ({ { title: 'Dynamically adding children to a container', id: 'embeddablePanelExamplae', - component: ( - - ), + component: , }, ]; diff --git a/examples/embeddable_explorer/public/embeddable_panel_example.tsx b/examples/embeddable_explorer/public/embeddable_panel_example.tsx index b26111bed7ff23..54cd7c5b5b2c01 100644 --- a/examples/embeddable_explorer/public/embeddable_panel_example.tsx +++ b/examples/embeddable_explorer/public/embeddable_panel_example.tsx @@ -29,43 +29,19 @@ import { EuiText, } from '@elastic/eui'; import { EuiSpacer } from '@elastic/eui'; -import { OverlayStart, CoreStart, SavedObjectsStart, IUiSettingsClient } from 'kibana/public'; -import { - EmbeddablePanel, - EmbeddableStart, - IEmbeddable, -} from '../../../src/plugins/embeddable/public'; +import { EmbeddableStart, IEmbeddable } from '../../../src/plugins/embeddable/public'; import { HELLO_WORLD_EMBEDDABLE, TODO_EMBEDDABLE, MULTI_TASK_TODO_EMBEDDABLE, SEARCHABLE_LIST_CONTAINER, } from '../../embeddable_examples/public'; -import { UiActionsStart } from '../../../src/plugins/ui_actions/public'; -import { Start as InspectorStartContract } from '../../../src/plugins/inspector/public'; -import { getSavedObjectFinder } from '../../../src/plugins/saved_objects/public'; interface Props { - getAllEmbeddableFactories: EmbeddableStart['getEmbeddableFactories']; - getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']; - uiActionsApi: UiActionsStart; - overlays: OverlayStart; - notifications: CoreStart['notifications']; - inspector: InspectorStartContract; - savedObject: SavedObjectsStart; - uiSettingsClient: IUiSettingsClient; + embeddableServices: EmbeddableStart; } -export function EmbeddablePanelExample({ - inspector, - notifications, - overlays, - getAllEmbeddableFactories, - getEmbeddableFactory, - uiActionsApi, - savedObject, - uiSettingsClient, -}: Props) { +export function EmbeddablePanelExample({ embeddableServices }: Props) { const searchableInput = { id: '1', title: 'My searchable todo list', @@ -105,7 +81,7 @@ export function EmbeddablePanelExample({ useEffect(() => { ref.current = true; if (!embeddable) { - const factory = getEmbeddableFactory(SEARCHABLE_LIST_CONTAINER); + const factory = embeddableServices.getEmbeddableFactory(SEARCHABLE_LIST_CONTAINER); const promise = factory?.create(searchableInput); if (promise) { promise.then(e => { @@ -134,22 +110,13 @@ export function EmbeddablePanelExample({ You can render your embeddable inside the EmbeddablePanel component. This adds some extra rendering and offers a context menu with pluggable actions. Using EmbeddablePanel - to render your embeddable means you get access to the "e;Add panel flyout"e;. - Now you can see how to add embeddables to your container, and how - "e;getExplicitInput"e; is used to grab input not provided by the container. + to render your embeddable means you get access to the "Add panel flyout". Now + you can see how to add embeddables to your container, and how + "getExplicitInput" is used to grab input not provided by the container. {embeddable ? ( - + ) : ( Loading... )} diff --git a/examples/embeddable_explorer/public/list_container_example.tsx b/examples/embeddable_explorer/public/list_container_example.tsx index 969fdb0ca46db8..98ad50418d3fea 100644 --- a/examples/embeddable_explorer/public/list_container_example.tsx +++ b/examples/embeddable_explorer/public/list_container_example.tsx @@ -29,7 +29,11 @@ import { EuiText, } from '@elastic/eui'; import { EuiSpacer } from '@elastic/eui'; -import { EmbeddableFactoryRenderer, EmbeddableStart } from '../../../src/plugins/embeddable/public'; +import { + EmbeddableFactoryRenderer, + EmbeddableStart, + ViewMode, +} from '../../../src/plugins/embeddable/public'; import { HELLO_WORLD_EMBEDDABLE, TODO_EMBEDDABLE, @@ -46,6 +50,7 @@ export function ListContainerExample({ getEmbeddableFactory }: Props) { const listInput = { id: 'hello', title: 'My todo list', + viewMode: ViewMode.VIEW, panels: { '1': { type: HELLO_WORLD_EMBEDDABLE, @@ -76,6 +81,7 @@ export function ListContainerExample({ getEmbeddableFactory }: Props) { const searchableInput = { id: '1', title: 'My searchable todo list', + viewMode: ViewMode.VIEW, panels: { '1': { type: HELLO_WORLD_EMBEDDABLE, @@ -150,7 +156,7 @@ export function ListContainerExample({ getEmbeddableFactory }: Props) {

- Check out the "e;Dynamically adding children"e; section, to see how to add + Check out the "Dynamically adding children" section, to see how to add children to this container, and see it rendered inside an `EmbeddablePanel` component.

diff --git a/package.json b/package.json index 46e0b9adfea251..bd0fec3a5c116e 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,8 @@ "examples/*", "test/plugin_functional/plugins/*", "test/interpreter_functional/plugins/*", - "x-pack/test/functional_with_es_ssl/fixtures/plugins/*" + "x-pack/test/functional_with_es_ssl/fixtures/plugins/*", + "x-pack/test/plugin_api_integration/plugins/*" ], "nohoist": [ "**/@types/*", @@ -125,7 +126,7 @@ "@elastic/filesaver": "1.1.2", "@elastic/good": "8.1.1-kibana2", "@elastic/numeral": "2.4.0", - "@elastic/request-crypto": "^1.0.2", + "@elastic/request-crypto": "1.1.2", "@elastic/ui-ace": "0.2.3", "@hapi/good-squeeze": "5.2.1", "@hapi/wreck": "^15.0.2", @@ -375,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", @@ -461,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", @@ -479,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 b648004760d7c7..b3e5a8c518682e 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 d52d89eebe2f13..4b4bb1282d9395 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 0cc1ad6326671d..7a858deff41d37 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 66f17ab579ec39..f4b91d154cbb88 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 d2305d643e4d20..1f1392b02baffa 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/chrome/ui/header/header_help_menu.tsx b/src/core/public/chrome/ui/header/header_help_menu.tsx index 80a45b4c61f077..1023a561a0fe33 100644 --- a/src/core/public/chrome/ui/header/header_help_menu.tsx +++ b/src/core/public/chrome/ui/header/header_help_menu.tsx @@ -314,13 +314,14 @@ class HeaderHelpMenuUI extends Component { return ( // @ts-ignore repositionOnScroll doesn't exist in EuiPopover diff --git a/src/core/public/plugins/plugin_loader.test.ts b/src/core/public/plugins/plugin_loader.test.ts index e5cbffc3e2d943..b4e2c3095f14a7 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 63aba0dde2af81..bf7711055e97ba 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 389d98a0818c8e..da8846f6dddbb9 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 b92a6edf778ed0..684f6e15caff98 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 cfd72a6fd5e47b..2e45f710c4dcfd 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 00000000000000..dd5fb04bfd1c63 --- /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 00000000000000..1eaa338af12399 --- /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 ef8edecfd26ec6..3d38935e9fbf0f 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 3e760ec0efabd6..7bd6331978d1de 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 56ce16a951aa26..a298f80f96d8f0 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 825deea99bc230..ede0d3dc9fcc71 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 c6860086e77842..0cf2ebe55ea10d 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 bb5f6d5617aaea..f77230301ce023 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 31bf17da041afb..faf73044cac4d1 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 32662f07a86f02..61d97aea97459c 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 b50e47b9eab738..fe4795cad11a5a 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 4fbadf90f4b600..466d399f653cd6 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 ddd82edd934484..5ba2d0afc692e8 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 dc966f0797822c..8ddaed3707eb0c 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 25772c4c9b0b16..df4751521ac533 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 2ee656721abd0b..257b32c1e4c237 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 fd82bf282266ef..336eeff99f47b6 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 bc29061b380b88..dafd6c53411966 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 9fe32b14e64507..7ba4613c857d7b 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 aa440c6454569a..62027928c0bb5d 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 00000000000000..8efea1e2c00c65 --- /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 00000000000000..66a6e2baa17a72 --- /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 962965a08f8b2a..f14e9d9efb5e3d 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 e4e2b8d7adbb77..f3e3b7736d8d30 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 53d1b742a64944..5d535c98457249 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 a4b5a9d81df203..24c41d511180a1 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 222be572b75e49..07ea431dd3a0df 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 00000000000000..7516e82ee784de --- /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 00000000000000..748a54f0bf8bba --- /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/collectors/index.js b/src/core/server/status/index.ts similarity index 91% rename from src/legacy/server/status/collectors/index.js rename to src/core/server/status/index.ts index 92d9e601bbb35e..c39115d55a6827 100644 --- a/src/legacy/server/status/collectors/index.js +++ b/src/core/server/status/index.ts @@ -17,4 +17,5 @@ * under the License. */ -export { registerOpsStatsCollector } from './get_ops_stats_collector'; +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 00000000000000..d550c2f06750bf --- /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 00000000000000..6d92a266369b90 --- /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 00000000000000..b6697d82219519 --- /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 00000000000000..765fa8771f3758 --- /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 00000000000000..84a7356c66bbf7 --- /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 470b1c2d135b70..f7e6fbcd0c131e 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 8bb3fb20cea8b7..ba3cd1280f34bb 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 029e1f149d0520..b7114f1029ef2c 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 95b14619c34169..cdff6cabad8ba8 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 023e6ebb7125ce..badea68eec19f9 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/migrations/migrations.js b/src/legacy/core_plugins/kibana/migrations/migrations.js index d37887c640b90b..029dbde555a4be 100644 --- a/src/legacy/core_plugins/kibana/migrations/migrations.js +++ b/src/legacy/core_plugins/kibana/migrations/migrations.js @@ -18,7 +18,10 @@ */ import { get } from 'lodash'; -import { migrations730 as dashboardMigrations730 } from '../public/dashboard/migrations'; +import { + migrateMatchAllQuery, + migrations730 as dashboardMigrations730, +} from '../public/dashboard/migrations'; function migrateIndexPattern(doc) { const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); @@ -60,6 +63,7 @@ function migrateIndexPattern(doc) { export const migrations = { dashboard: { + '6.7.2': migrateMatchAllQuery, '7.0.0': doc => { // Set new "references" attribute doc.references = doc.references || []; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/migrations/index.ts b/src/legacy/core_plugins/kibana/public/dashboard/migrations/index.ts index da2542e854c323..f333ce97d120f6 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/migrations/index.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/migrations/index.ts @@ -18,3 +18,4 @@ */ export { migrations730 } from './migrations_730'; +export { migrateMatchAllQuery } from './migrate_match_all_query'; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/migrations/migrate_match_all_query.test.ts b/src/legacy/core_plugins/kibana/public/dashboard/migrations/migrate_match_all_query.test.ts new file mode 100644 index 00000000000000..8a91c422eed3df --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/dashboard/migrations/migrate_match_all_query.test.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 { migrateMatchAllQuery } from './migrate_match_all_query'; +import { SavedObjectMigrationContext, SavedObjectMigrationFn } from 'kibana/server'; + +const savedObjectMigrationContext = (null as unknown) as SavedObjectMigrationContext; + +describe('migrate match_all query', () => { + test('should migrate obsolete match_all query', () => { + const migratedDoc = migrateMatchAllQuery( + { + attributes: { + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ + query: { + match_all: {}, + }, + }), + }, + }, + } as Parameters[0], + savedObjectMigrationContext + ); + + const migratedSearchSource = JSON.parse( + migratedDoc.attributes.kibanaSavedObjectMeta.searchSourceJSON + ); + + expect(migratedSearchSource).toEqual({ + query: { + query: '', + language: 'kuery', + }, + }); + }); +}); diff --git a/src/legacy/core_plugins/kibana/public/dashboard/migrations/migrate_match_all_query.ts b/src/legacy/core_plugins/kibana/public/dashboard/migrations/migrate_match_all_query.ts new file mode 100644 index 00000000000000..707aae9e5d4acc --- /dev/null +++ b/src/legacy/core_plugins/kibana/public/dashboard/migrations/migrate_match_all_query.ts @@ -0,0 +1,56 @@ +/* + * 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 { SavedObjectMigrationFn } from 'kibana/server'; +import { get } from 'lodash'; +import { DEFAULT_QUERY_LANGUAGE } from '../../../../../../plugins/data/common'; + +export const migrateMatchAllQuery: SavedObjectMigrationFn = doc => { + const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); + + if (searchSourceJSON) { + let searchSource: any; + + try { + searchSource = JSON.parse(searchSourceJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + + if (searchSource.query?.match_all) { + return { + ...doc, + attributes: { + ...doc.attributes, + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ + ...searchSource, + query: { + query: '', + language: DEFAULT_QUERY_LANGUAGE, + }, + }), + }, + }, + }; + } + } + + return doc; +}; 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 607d79b81618e9..6ccbc13aeeb571 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 55f369eaecd2c2..98679a8f24d16a 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 57ad8e0b1040f3..8fcfcba08955c3 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 697b039adde818..b705b4e4faeb59 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 5482258e06564f..5a999235bf9a5c 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 ba02068590c149..ad81d5252a25c0 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 f40865800098eb..b308607bbfbb07 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/doc.ts b/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/doc.ts index 459dcfb30d17b9..092e3c79b1007c 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 f21f3b17c69558..7e155a6b82ca08 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 608dadd36b4b9a..84338d817c86b5 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 84d865fd22a9a3..5e7ad6a1d1ea0b 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 28a17dbdb67b7f..f3ceaef57d7008 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 1aad7e953b8de0..f8fc966dec3519 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 5984df9c76e61f..0adda0e484843a 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 9dcb459f83613d..624ec0f7578947 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/server/status/constants.js b/src/legacy/core_plugins/kibana/public/index.ts similarity index 86% rename from src/legacy/server/status/constants.js rename to src/legacy/core_plugins/kibana/public/index.ts index 3bb23749bae874..a4fffc6eec26da 100644 --- a/src/legacy/server/status/constants.js +++ b/src/legacy/core_plugins/kibana/public/index.ts @@ -17,4 +17,7 @@ * under the License. */ -export const KIBANA_STATS_TYPE = 'oss_kibana_stats'; // kibana stats per 5s intervals +export { + ProcessedImportResponse, + processImportResponse, +} from './management/sections/objects/lib/process_import_response'; 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 25bd36829b6d03..40471b95d774c1 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 bbb6bf26e5b311..648bf7f8f97388 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 e0c43105cb320f..b23b1e3ad9051e 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 80582cc1fbd921..a58bf10c9dab8b 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 50c5a58d35db3b..47cb773258cb46 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 5a8460fcb51bae..40583af7177fee 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 3848c425e2d495..3b1b7a3b52a5b8 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 6d302ac5a74f35..594430ca01f4ce 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 310797a7f3a0cc..a8376c0e84bf9b 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 a5e34f8955fe30..7b9c17640a0f3e 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 97c0d5b89d657b..5d14c4609b918c 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(), })); diff --git a/src/legacy/core_plugins/kibana/public/visualize/_index.scss b/src/legacy/core_plugins/kibana/public/visualize/_index.scss index 0632831578bd01..079d82936bb574 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 addc608efd57d8..d5440c4677d8ae 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/plugin.ts b/src/legacy/core_plugins/kibana/public/visualize/plugin.ts index 81d8458010f196..4ffbc307c69a83 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/package.json b/src/legacy/core_plugins/management/package.json deleted file mode 100644 index 77d33a7bce3b6c..00000000000000 --- 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/index.ts b/src/legacy/core_plugins/management/public/index.ts deleted file mode 100644 index bc3737524e1259..00000000000000 --- a/src/legacy/core_plugins/management/public/index.ts +++ /dev/null @@ -1,38 +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. - */ - -/** - * 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'; 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 96d2c74398a0e8..00000000000000 --- 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 4219e2e7151508..61cfbf00ded9e6 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 4faa3f92abb5a6..9174b03cf843cf 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 25641ea76809df..72f9d66e7d2bf4 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 168f56b771b7e5..9169647aa2aef2 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 39d39a4c8f8fcd..fe82ad5c7352b2 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/vis_default_editor/package.json b/src/legacy/core_plugins/vis_default_editor/package.json deleted file mode 100644 index 77dcaff41da6bc..00000000000000 --- 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 8a4297d3b8149c..a6349793619a01 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 b84d9638eb973d..57ea6d9c9bb3d6 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 ac1d4bcc82ceca..552fd633735543 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 661f16d6497ba3..5c3032511f09af 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 22c32895d68035..4094cd4eff0601 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 459da47556307c..706693eff10072 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 f29164f7e540d1..3bbb8964122e54 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 5af11bc7f0b039..b303ccd5aeed20 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 30a95262731668..d01ab31e0a843b 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 08efed733cafe5..b4b491ac7a5552 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 d26e860e51272a..43816121bc23bc 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 a9e816f70cf531..80e4e1de7ddab6 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 fef46282eb8ddd..272bed3e91a08c 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 5a8cc3004a3154..b7dfa62c93fb97 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 6e29b111d422ac..8a8e1b22fb78db 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 b7c40e15c11fd4..afffcf7ccaf7a7 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 6679553004097b..5be77b3e51a6ad 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/index.ts b/src/legacy/core_plugins/vis_type_timeseries/index.ts index 3ad8ba3a31c17e..596fd5b581a719 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/index.ts +++ b/src/legacy/core_plugins/vis_type_timeseries/index.ts @@ -31,20 +31,6 @@ const metricsPluginInitializer: LegacyPluginInitializer = ({ Plugin }: LegacyPlu styleSheetPaths: resolve(__dirname, 'public/index.scss'), hacks: [resolve(__dirname, 'public/legacy')], injectDefaultVars: server => ({}), - mappings: { - 'tsvb-validation-telemetry': { - properties: { - failedRequests: { - type: 'long', - }, - }, - }, - }, - savedObjectSchemas: { - 'tsvb-validation-telemetry': { - isNamespaceAgnostic: true, - }, - }, }, config(Joi: any) { return Joi.object({ 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 30c62d778933bf..1db35c406eb132 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 64a9aaaf3b7a68..b2f3e5b2241e61 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 707a6830b5ba45..31144065d7b40b 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 b0ec90d2c378f1..f56d7682efc6f9 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 e79555470298b8..68decacaaa0404 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 1138f66d21cfa5..baf3e8ecd1b289 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 2c9b1b543e8c23..84c70f10b12da7 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 06ce0a2b4af64b..35d7f7b13235e4 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 c069d4c935669e..718056fd85492b 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 706035a7b814ed..6109b548f9412c 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 452b9ed9bdbb16..715b5902b69da2 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 c74f0ef765c8dc..38811bd8364595 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 442bc826d51fcc..09e0753d592e52 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 049df0cdd77be9..915885388640c2 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 a3f150e7188175..524792d1460fe3 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 c7b4562b1087e4..114305d653dd1c 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 2182edafb3ebf2..4c0be456aad64b 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 db9acafac305c6..bb2b3f8fddb492 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 4610bd37db5f1a..7ad821dbf2f30a 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 c918128d01f110..6c311bebe07172 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 39a583f3c96411..88b4f0fcaf87e0 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 15ef369e5150e2..54ec8f98203e25 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 8b5811628855c8..dc47252ccd44fe 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 ac4cda869fe295..885ab295d11e1f 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 0f1bd93f5b5bd5..2774836baa3817 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 da50e227d84d20..0d6b1b5e8de589 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 6da40686a8b506..de867dc72bba7a 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 b733e2e721e3a5..00000000000000 --- 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 df02b3c45ec2f8..5bd1efa99eb2c6 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 25647e4a088971..0779d6472671cb 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 @@ -88,6 +88,9 @@ const mockCoreStart = { get: sinon.fake.returns(''), }, }, + notifications: { + toasts: {}, + }, i18n: {}, overlays: {}, savedObjects: { @@ -164,8 +167,11 @@ const mockAggTypesRegistry = () => { const registrySetup = registry.setup(); const aggTypes = getAggTypes({ uiSettings: mockCoreSetup.uiSettings, - notifications: mockCoreStart.notifications, query: querySetup, + getInternalStartServices: () => ({ + fieldFormats: getFieldFormatsRegistry(mockCoreStart), + notifications: mockCoreStart.notifications, + }), }); aggTypes.buckets.forEach(type => registrySetup.registerBucket(type)); aggTypes.metrics.forEach(type => registrySetup.registerMetric(type)); @@ -284,6 +290,10 @@ export const npSetup = { }), }, }, + indexPatternManagement: { + list: { addListConfig: sinon.fake() }, + creation: { addCreationConfig: sinon.fake() }, + }, discover: { docViews: { addDocView: sinon.fake(), @@ -319,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 b4b5099081759e..cdd7e1a9949127 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/ui_render/bootstrap/template.js.hbs b/src/legacy/ui/ui_render/bootstrap/template.js.hbs index ad4aa97d8ea7a0..1093153edbbf7e 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/data/common/field_formats/mocks.ts b/src/plugins/data/common/field_formats/mocks.ts index bc38374e147cf6..394d4c383032fb 100644 --- a/src/plugins/data/common/field_formats/mocks.ts +++ b/src/plugins/data/common/field_formats/mocks.ts @@ -17,23 +17,14 @@ * under the License. */ -import { FieldFormat, IFieldFormatsRegistry } from '.'; - -const fieldFormatMock = ({ - convert: jest.fn(), - getConverterFor: jest.fn(), - getParamDefaults: jest.fn(), - param: jest.fn(), - params: jest.fn(), - toJSON: jest.fn(), - type: jest.fn(), - setupContentType: jest.fn(), -} as unknown) as FieldFormat; +import { IFieldFormatsRegistry } from '.'; export const fieldFormatsMock: IFieldFormatsRegistry = { getByFieldType: jest.fn(), getDefaultConfig: jest.fn(), - getDefaultInstance: jest.fn().mockImplementation(() => fieldFormatMock) as any, + getDefaultInstance: jest.fn().mockImplementation(() => ({ + getConverterFor: jest.fn().mockImplementation(() => (t: string) => t), + })) as any, getDefaultInstanceCacheResolver: jest.fn(), getDefaultInstancePlain: jest.fn(), getDefaultType: jest.fn(), diff --git a/src/legacy/core_plugins/vis_default_editor/index.ts b/src/plugins/data/public/field_formats/mocks.ts similarity index 50% rename from src/legacy/core_plugins/vis_default_editor/index.ts rename to src/plugins/data/public/field_formats/mocks.ts index ee7b5ee4a62ffb..ec1233a085bce3 100644 --- a/src/legacy/core_plugins/vis_default_editor/index.ts +++ b/src/plugins/data/public/field_formats/mocks.ts @@ -17,26 +17,25 @@ * under the License. */ -import { resolve } from 'path'; -import { Legacy } from 'kibana'; +import { FieldFormatsStart, FieldFormatsSetup, FieldFormatsService } from '.'; +import { fieldFormatsMock } from '../../common/field_formats/mocks'; -import { LegacyPluginApi, LegacyPluginInitializer } from '../../../../src/legacy/types'; +type FieldFormatsServiceClientContract = PublicMethodsOf; -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); +const createSetupContractMock = () => fieldFormatsMock as FieldFormatsSetup; +const createStartContractMock = () => fieldFormatsMock as FieldFormatsStart; -// eslint-disable-next-line import/no-default-export -export default vidDefaultEditorPluginInitializer; +const createMock = () => { + const mocked: jest.Mocked = { + setup: jest.fn().mockReturnValue(createSetupContractMock()), + start: jest.fn().mockReturnValue(createStartContractMock()), + }; + + return mocked; +}; + +export const fieldFormatsServiceMock = { + create: createMock, + createSetupContract: createSetupContractMock, + createStartContract: createStartContractMock, +}; diff --git a/src/plugins/data/public/index_patterns/index_patterns/index_patterns_api_client.ts b/src/plugins/data/public/index_patterns/index_patterns/index_patterns_api_client.ts index 4d4e8d8827b483..0007d1780c25bc 100644 --- a/src/plugins/data/public/index_patterns/index_patterns/index_patterns_api_client.ts +++ b/src/plugins/data/public/index_patterns/index_patterns/index_patterns_api_client.ts @@ -45,7 +45,7 @@ export class IndexPatternsApiClient { query, }) .catch((resp: any) => { - if (resp.body.statusCode === 404 && resp.body.statuscode === 'no_matching_indices') { + if (resp.body.statusCode === 404 && resp.body.attributes?.code === 'no_matching_indices') { throw new IndexPatternMissingIndices(resp.body.message); } diff --git a/src/plugins/data/public/mocks.ts b/src/plugins/data/public/mocks.ts index e3fc0e97af09b3..ea1c27550867ee 100644 --- a/src/plugins/data/public/mocks.ts +++ b/src/plugins/data/public/mocks.ts @@ -17,8 +17,8 @@ * under the License. */ -import { Plugin, DataPublicPluginSetup, DataPublicPluginStart, IndexPatternsContract } from '.'; -import { fieldFormatsMock } from '../common/field_formats/mocks'; +import { Plugin, IndexPatternsContract } from '.'; +import { fieldFormatsServiceMock } from './field_formats/mocks'; import { searchSetupMock, searchStartMock } from './search/mocks'; import { queryServiceMock } from './query/mocks'; @@ -36,7 +36,7 @@ const createSetupContract = (): Setup => { return { autocomplete: autocompleteMock, search: searchSetupMock, - fieldFormats: fieldFormatsMock as DataPublicPluginSetup['fieldFormats'], + fieldFormats: fieldFormatsServiceMock.createSetupContract(), query: querySetupMock, }; }; @@ -49,7 +49,7 @@ const createStartContract = (): Start => { }, autocomplete: autocompleteMock, search: searchStartMock, - fieldFormats: fieldFormatsMock as DataPublicPluginStart['fieldFormats'], + fieldFormats: fieldFormatsServiceMock.createStartContract(), query: queryStartMock, ui: { IndexPatternSelect: jest.fn(), diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts index 26587470adfd99..15067077afc43b 100644 --- a/src/plugins/data/public/plugin.ts +++ b/src/plugins/data/public/plugin.ts @@ -30,6 +30,7 @@ import { DataPublicPluginStart, DataSetupDependencies, DataStartDependencies, + GetInternalStartServicesFn, } from './types'; import { AutocompleteService } from './autocomplete'; import { SearchService } from './search/search_service'; @@ -47,6 +48,8 @@ import { setQueryService, setSearchService, setUiSettings, + getFieldFormats, + getNotifications, } from './services'; import { createSearchBar } from './ui/search_bar/create_search_bar'; import { esaggs } from './search/expressions'; @@ -100,6 +103,11 @@ export class DataPublicPlugin implements Plugin ({ + fieldFormats: getFieldFormats(), + notifications: getNotifications(), + }); + const queryService = this.queryService.setup({ uiSettings: core.uiSettings, storage: this.storage, @@ -122,6 +130,7 @@ export class DataPublicPlugin implements Plugin { +export interface IDataPluginServices extends Partial { // (undocumented) appName: string; // (undocumented) data: DataPublicPluginStart; // (undocumented) - http: CoreStart_2['http']; + http: CoreStart['http']; // (undocumented) - notifications: CoreStart_2['notifications']; + notifications: CoreStart['notifications']; // (undocumented) - savedObjects: CoreStart_2['savedObjects']; + savedObjects: CoreStart['savedObjects']; // (undocumented) storage: IStorageWrapper; // (undocumented) - uiSettings: CoreStart_2['uiSettings']; + uiSettings: CoreStart['uiSettings']; } // Warning: (ae-missing-release-tag) "IEsSearchRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1094,7 +1094,7 @@ export type ISearch = // @public (undocumented) export interface ISearchContext { // (undocumented) - core: CoreStart; + core: CoreStart_2; // (undocumented) getSearchStrategy: (name: T) => TSearchStrategyProvider; } @@ -1307,7 +1307,7 @@ export class Plugin implements Plugin_2 { + describe('model presentation formatter', () => { + test('should present objects as strings', () => { expect(toUser({ foo: 'bar' })).toBe('{"foo":"bar"}'); }); - it('should present query_string queries as strings', function() { + test('should present query_string queries as strings', () => { expect(toUser({ query_string: { query: 'lucene query string' } })).toBe( 'lucene query string' ); }); - it('should present query_string queries without a query as an empty string', function() { + test('should present query_string queries without a query as an empty string', () => { expect(toUser({ query_string: {} })).toBe(''); }); - it('should present string as strings', function() { + test('should present string as strings', () => { expect(toUser('foo')).toBe('foo'); }); - it('should present numbers as strings', function() { + test('should present numbers as strings', () => { expect(toUser(400)).toBe('400'); }); }); diff --git a/src/plugins/data/public/query/lib/to_user.ts b/src/plugins/data/public/query/lib/to_user.ts index 1fdb2d8ed03df1..1a364534d93fbf 100644 --- a/src/plugins/data/public/query/lib/to_user.ts +++ b/src/plugins/data/public/query/lib/to_user.ts @@ -27,9 +27,6 @@ export function toUser(text: { [key: string]: any } | string | number): string { return ''; } if (typeof text === 'object') { - if (text.match_all) { - return ''; - } if (text.query_string) { return toUser(text.query_string.query); } diff --git a/src/plugins/data/public/search/aggs/agg_config.test.ts b/src/plugins/data/public/search/aggs/agg_config.test.ts index f979c9664f458a..41d943ef0c94ba 100644 --- a/src/plugins/data/public/search/aggs/agg_config.test.ts +++ b/src/plugins/data/public/search/aggs/agg_config.test.ts @@ -26,8 +26,6 @@ import { AggTypesRegistryStart } from './agg_types_registry'; import { mockDataServices, mockAggTypesRegistry } from './test_helpers'; import { Field as IndexPatternField, IndexPattern } from '../../index_patterns'; import { stubIndexPatternWithFields } from '../../../public/stubs'; -import { dataPluginMock } from '../../../public/mocks'; -import { setFieldFormats } from '../../../public/services'; describe('AggConfig', () => { let indexPattern: IndexPattern; @@ -400,13 +398,6 @@ describe('AggConfig', () => { describe('#fieldFormatter - custom getFormat handler', () => { it('returns formatter from getFormat handler', () => { - setFieldFormats({ - ...dataPluginMock.createStartContract().fieldFormats, - getDefaultInstance: jest.fn().mockImplementation(() => ({ - getConverterFor: jest.fn().mockImplementation(() => (t: string) => t), - })) as any, - }); - const ac = new AggConfigs(indexPattern, [], { typesRegistry }); const configStates = { enabled: true, @@ -429,12 +420,6 @@ describe('AggConfig', () => { let aggConfig: AggConfig; beforeEach(() => { - setFieldFormats({ - ...dataPluginMock.createStartContract().fieldFormats, - getDefaultInstance: jest.fn().mockImplementation(() => ({ - getConverterFor: (t?: string) => t || identity, - })) as any, - }); indexPattern.fields.getByName = name => ({ format: { diff --git a/src/plugins/data/public/search/aggs/agg_params.test.ts b/src/plugins/data/public/search/aggs/agg_params.test.ts index b08fcf309e9ed6..784be803e2644f 100644 --- a/src/plugins/data/public/search/aggs/agg_params.test.ts +++ b/src/plugins/data/public/search/aggs/agg_params.test.ts @@ -22,12 +22,22 @@ import { BaseParamType } from './param_types/base'; import { FieldParamType } from './param_types/field'; import { OptionedParamType } from './param_types/optioned'; import { AggParamType } from '../aggs/param_types/agg'; +import { fieldFormatsServiceMock } from '../../field_formats/mocks'; +import { notificationServiceMock } from '../../../../../../src/core/public/mocks'; +import { AggTypeDependencies } from './agg_type'; describe('AggParams class', () => { + const aggTypesDependencies: AggTypeDependencies = { + getInternalStartServices: () => ({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + }), + }; + describe('constructor args', () => { it('accepts an array of param defs', () => { const params = [{ name: 'one' }, { name: 'two' }] as AggParamType[]; - const aggParams = initParams(params); + const aggParams = initParams(params, aggTypesDependencies); expect(aggParams).toHaveLength(params.length); expect(Array.isArray(aggParams)).toBeTruthy(); @@ -37,7 +47,7 @@ describe('AggParams class', () => { describe('AggParam creation', () => { it('Uses the FieldParamType class for params with the name "field"', () => { const params = [{ name: 'field', type: 'field' }] as AggParamType[]; - const aggParams = initParams(params); + const aggParams = initParams(params, aggTypesDependencies); expect(aggParams).toHaveLength(params.length); expect(aggParams[0] instanceof FieldParamType).toBeTruthy(); @@ -50,7 +60,7 @@ describe('AggParams class', () => { type: 'optioned', }, ] as AggParamType[]; - const aggParams = initParams(params); + const aggParams = initParams(params, aggTypesDependencies); expect(aggParams).toHaveLength(params.length); expect(aggParams[0] instanceof OptionedParamType).toBeTruthy(); @@ -72,7 +82,7 @@ describe('AggParams class', () => { }, ] as AggParamType[]; - const aggParams = initParams(params); + const aggParams = initParams(params, aggTypesDependencies); expect(aggParams).toHaveLength(params.length); diff --git a/src/plugins/data/public/search/aggs/agg_params.ts b/src/plugins/data/public/search/aggs/agg_params.ts index 551cb81529a0a3..e7b2f72bae656d 100644 --- a/src/plugins/data/public/search/aggs/agg_params.ts +++ b/src/plugins/data/public/search/aggs/agg_params.ts @@ -26,6 +26,7 @@ import { BaseParamType } from './param_types/base'; import { AggConfig } from './agg_config'; import { IAggConfigs } from './agg_configs'; +import { AggTypeDependencies } from './agg_type'; const paramTypeMap = { field: FieldParamType, @@ -45,12 +46,13 @@ export interface AggParamOption { } export const initParams = ( - params: TAggParam[] + params: TAggParam[], + { getInternalStartServices }: AggTypeDependencies ): TAggParam[] => params.map((config: TAggParam) => { const Class = paramTypeMap[config.type] || paramTypeMap._default; - return new Class(config); + return new Class(config, { getInternalStartServices }); }) as TAggParam[]; /** diff --git a/src/plugins/data/public/search/aggs/agg_type.test.ts b/src/plugins/data/public/search/aggs/agg_type.test.ts index 3fb03dc31e2b2b..0c9e110c34ae66 100644 --- a/src/plugins/data/public/search/aggs/agg_type.test.ts +++ b/src/plugins/data/public/search/aggs/agg_type.test.ts @@ -17,40 +17,49 @@ * under the License. */ -import { AggType, AggTypeConfig } from './agg_type'; +import { AggType, AggTypeConfig, AggTypeDependencies } from './agg_type'; import { IAggConfig } from './agg_config'; -import { mockDataServices } from './test_helpers'; -import { dataPluginMock } from '../../../public/mocks'; -import { setFieldFormats } from '../../../public/services'; +import { fieldFormatsServiceMock } from '../../field_formats/mocks'; +import { notificationServiceMock } from '../../../../../../src/core/public/mocks'; describe('AggType Class', () => { + let dependencies: AggTypeDependencies; + beforeEach(() => { - mockDataServices(); + dependencies = { + getInternalStartServices: () => ({ + fieldFormats: { + ...fieldFormatsServiceMock.createStartContract(), + getDefaultInstance: jest.fn(() => 'default') as any, + }, + notifications: notificationServiceMock.createStartContract(), + }), + }; }); describe('constructor', () => { - it("requires a valid config object as it's first param", () => { + test("requires a valid config object as it's first param", () => { expect(() => { const aggConfig: AggTypeConfig = (undefined as unknown) as AggTypeConfig; - new AggType(aggConfig); + new AggType(aggConfig, dependencies); }).toThrowError(); }); describe('application of config properties', () => { - it('assigns the config value to itself', () => { + test('assigns the config value to itself', () => { const config: AggTypeConfig = { name: 'name', title: 'title', }; - const aggType = new AggType(config); + const aggType = new AggType(config, dependencies); expect(aggType.name).toBe('name'); expect(aggType.title).toBe('title'); }); describe('makeLabel', () => { - it('makes a function when the makeLabel config is not specified', () => { + test('makes a function when the makeLabel config is not specified', () => { const makeLabel = () => 'label'; const aggConfig = {} as IAggConfig; const config: AggTypeConfig = { @@ -59,7 +68,7 @@ describe('AggType Class', () => { makeLabel, }; - const aggType = new AggType(config); + const aggType = new AggType(config, dependencies); expect(aggType.makeLabel).toBe(makeLabel); expect(aggType.makeLabel(aggConfig)).toBe('label'); @@ -67,26 +76,32 @@ describe('AggType Class', () => { }); describe('getResponseAggs/getRequestAggs', () => { - it('copies the value', () => { + test('copies the value', () => { const testConfig = (aggConfig: IAggConfig) => [aggConfig]; - const aggType = new AggType({ - name: 'name', - title: 'title', - getResponseAggs: testConfig, - getRequestAggs: testConfig, - }); + const aggType = new AggType( + { + name: 'name', + title: 'title', + getResponseAggs: testConfig, + getRequestAggs: testConfig, + }, + dependencies + ); expect(aggType.getResponseAggs).toBe(testConfig); expect(aggType.getResponseAggs).toBe(testConfig); }); - it('defaults to noop', () => { + test('defaults to noop', () => { const aggConfig = {} as IAggConfig; - const aggType = new AggType({ - name: 'name', - title: 'title', - }); + const aggType = new AggType( + { + name: 'name', + title: 'title', + }, + dependencies + ); const responseAggs = aggType.getRequestAggs(aggConfig); expect(responseAggs).toBe(undefined); @@ -94,11 +109,14 @@ describe('AggType Class', () => { }); describe('params', () => { - it('defaults to AggParams object with JSON param', () => { - const aggType = new AggType({ - name: 'smart agg', - title: 'title', - }); + test('defaults to AggParams object with JSON param', () => { + const aggType = new AggType( + { + name: 'smart agg', + title: 'title', + }, + dependencies + ); expect(Array.isArray(aggType.params)).toBeTruthy(); expect(aggType.params.length).toBe(2); @@ -106,26 +124,32 @@ describe('AggType Class', () => { expect(aggType.params[1].name).toBe('customLabel'); }); - it('can disable customLabel', () => { - const aggType = new AggType({ - name: 'smart agg', - title: 'title', - customLabels: false, - }); + test('can disable customLabel', () => { + const aggType = new AggType( + { + name: 'smart agg', + title: 'title', + customLabels: false, + }, + dependencies + ); expect(aggType.params.length).toBe(1); expect(aggType.params[0].name).toBe('json'); }); - it('passes the params arg directly to the AggParams constructor', () => { + test('passes the params arg directly to the AggParams constructor', () => { const params = [{ name: 'one' }, { name: 'two' }]; const paramLength = params.length + 2; // json and custom label are always appended - const aggType = new AggType({ - name: 'bucketeer', - title: 'title', - params, - }); + const aggType = new AggType( + { + name: 'bucketeer', + title: 'title', + params, + }, + dependencies + ); expect(Array.isArray(aggType.params)).toBeTruthy(); expect(aggType.params.length).toBe(paramLength); @@ -143,11 +167,14 @@ describe('AggType Class', () => { } as unknown) as IAggConfig; }); - it('returns the formatter for the aggConfig', () => { - const aggType = new AggType({ - name: 'name', - title: 'title', - }); + test('returns the formatter for the aggConfig', () => { + const aggType = new AggType( + { + name: 'name', + title: 'title', + }, + dependencies + ); field = { format: 'format', @@ -156,16 +183,14 @@ describe('AggType Class', () => { expect(aggType.getFormat(aggConfig)).toBe('format'); }); - it('returns default formatter', () => { - setFieldFormats({ - ...dataPluginMock.createStartContract().fieldFormats, - getDefaultInstance: jest.fn(() => 'default') as any, - }); - - const aggType = new AggType({ - name: 'name', - title: 'title', - }); + test('returns default formatter', () => { + const aggType = new AggType( + { + name: 'name', + title: 'title', + }, + dependencies + ); field = undefined; diff --git a/src/plugins/data/public/search/aggs/agg_type.ts b/src/plugins/data/public/search/aggs/agg_type.ts index a63d01e196612a..70c116d560c6f3 100644 --- a/src/plugins/data/public/search/aggs/agg_type.ts +++ b/src/plugins/data/public/search/aggs/agg_type.ts @@ -28,7 +28,7 @@ import { BaseParamType } from './param_types/base'; import { AggParamType } from './param_types/agg'; import { KBN_FIELD_TYPES, IFieldFormat } from '../../../common'; import { ISearchSource } from '../search_source'; -import { getFieldFormats } from '../../../public/services'; +import { GetInternalStartServicesFn } from '../../types'; export interface AggTypeConfig< TAggConfig extends AggConfig = AggConfig, @@ -60,16 +60,13 @@ export interface AggTypeConfig< getKey?: (bucket: any, key: any, agg: TAggConfig) => any; } -const getFormat = (agg: AggConfig) => { - const field = agg.getField(); - const fieldFormatsService = getFieldFormats(); - - return field ? field.format : fieldFormatsService.getDefaultInstance(KBN_FIELD_TYPES.STRING); -}; - // TODO need to make a more explicit interface for this export type IAggType = AggType; +export interface AggTypeDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} + export class AggType< TAggConfig extends AggConfig = AggConfig, TParam extends AggParamType = AggParamType @@ -215,7 +212,10 @@ export class AggType< * @private * @param {object} config - used to set the properties of the AggType */ - constructor(config: AggTypeConfig) { + constructor( + config: AggTypeConfig, + { getInternalStartServices }: AggTypeDependencies + ) { this.name = config.name; this.type = config.type || 'metrics'; this.dslName = config.dslName || config.name; @@ -251,14 +251,22 @@ export class AggType< }); } - this.params = initParams(params); + this.params = initParams(params, { getInternalStartServices }); } this.getRequestAggs = config.getRequestAggs || noop; this.getResponseAggs = config.getResponseAggs || (() => {}); this.decorateAggConfig = config.decorateAggConfig || (() => ({})); this.postFlightRequest = config.postFlightRequest || identity; - this.getFormat = config.getFormat || getFormat; + + this.getFormat = + config.getFormat || + ((agg: TAggConfig) => { + const field = agg.getField(); + const { fieldFormats } = getInternalStartServices(); + + return field ? field.format : fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.STRING); + }); this.getValue = config.getValue || ((agg: TAggConfig, bucket: any) => {}); } } diff --git a/src/plugins/data/public/search/aggs/agg_types.ts b/src/plugins/data/public/search/aggs/agg_types.ts index 556f6b0c93c414..4b154c338d48c2 100644 --- a/src/plugins/data/public/search/aggs/agg_types.ts +++ b/src/plugins/data/public/search/aggs/agg_types.ts @@ -17,83 +17,89 @@ * under the License. */ -import { IUiSettingsClient, NotificationsSetup } from 'src/core/public'; +import { IUiSettingsClient } from 'src/core/public'; import { QuerySetup } from '../../query/query_service'; -import { countMetricAgg } from './metrics/count'; -import { avgMetricAgg } from './metrics/avg'; -import { sumMetricAgg } from './metrics/sum'; -import { medianMetricAgg } from './metrics/median'; -import { minMetricAgg } from './metrics/min'; -import { maxMetricAgg } from './metrics/max'; -import { topHitMetricAgg } from './metrics/top_hit'; -import { stdDeviationMetricAgg } from './metrics/std_deviation'; -import { cardinalityMetricAgg } from './metrics/cardinality'; -import { percentilesMetricAgg } from './metrics/percentiles'; -import { geoBoundsMetricAgg } from './metrics/geo_bounds'; -import { geoCentroidMetricAgg } from './metrics/geo_centroid'; -import { percentileRanksMetricAgg } from './metrics/percentile_ranks'; -import { derivativeMetricAgg } from './metrics/derivative'; -import { cumulativeSumMetricAgg } from './metrics/cumulative_sum'; -import { movingAvgMetricAgg } from './metrics/moving_avg'; -import { serialDiffMetricAgg } from './metrics/serial_diff'; +import { getCountMetricAgg } from './metrics/count'; +import { getAvgMetricAgg } from './metrics/avg'; +import { getSumMetricAgg } from './metrics/sum'; +import { getMedianMetricAgg } from './metrics/median'; +import { getMinMetricAgg } from './metrics/min'; +import { getMaxMetricAgg } from './metrics/max'; +import { getTopHitMetricAgg } from './metrics/top_hit'; +import { getStdDeviationMetricAgg } from './metrics/std_deviation'; +import { getCardinalityMetricAgg } from './metrics/cardinality'; +import { getPercentilesMetricAgg } from './metrics/percentiles'; +import { getGeoBoundsMetricAgg } from './metrics/geo_bounds'; +import { getGeoCentroidMetricAgg } from './metrics/geo_centroid'; +import { getPercentileRanksMetricAgg } from './metrics/percentile_ranks'; +import { getDerivativeMetricAgg } from './metrics/derivative'; +import { getCumulativeSumMetricAgg } from './metrics/cumulative_sum'; +import { getMovingAvgMetricAgg } from './metrics/moving_avg'; +import { getSerialDiffMetricAgg } from './metrics/serial_diff'; import { getDateHistogramBucketAgg } from './buckets/date_histogram'; import { getHistogramBucketAgg } from './buckets/histogram'; -import { rangeBucketAgg } from './buckets/range'; +import { getRangeBucketAgg } from './buckets/range'; import { getDateRangeBucketAgg } from './buckets/date_range'; -import { ipRangeBucketAgg } from './buckets/ip_range'; -import { termsBucketAgg } from './buckets/terms'; -import { filterBucketAgg } from './buckets/filter'; +import { getIpRangeBucketAgg } from './buckets/ip_range'; +import { getTermsBucketAgg } from './buckets/terms'; +import { getFilterBucketAgg } from './buckets/filter'; import { getFiltersBucketAgg } from './buckets/filters'; -import { significantTermsBucketAgg } from './buckets/significant_terms'; -import { geoHashBucketAgg } from './buckets/geo_hash'; -import { geoTileBucketAgg } from './buckets/geo_tile'; -import { bucketSumMetricAgg } from './metrics/bucket_sum'; -import { bucketAvgMetricAgg } from './metrics/bucket_avg'; -import { bucketMinMetricAgg } from './metrics/bucket_min'; -import { bucketMaxMetricAgg } from './metrics/bucket_max'; +import { getSignificantTermsBucketAgg } from './buckets/significant_terms'; +import { getGeoHashBucketAgg } from './buckets/geo_hash'; +import { getGeoTitleBucketAgg } from './buckets/geo_tile'; +import { getBucketSumMetricAgg } from './metrics/bucket_sum'; +import { getBucketAvgMetricAgg } from './metrics/bucket_avg'; +import { getBucketMinMetricAgg } from './metrics/bucket_min'; +import { getBucketMaxMetricAgg } from './metrics/bucket_max'; + +import { GetInternalStartServicesFn } from '../../types'; export interface AggTypesDependencies { - notifications: NotificationsSetup; uiSettings: IUiSettingsClient; query: QuerySetup; + getInternalStartServices: GetInternalStartServicesFn; } -export const getAggTypes = ({ notifications, uiSettings, query }: AggTypesDependencies) => ({ +export const getAggTypes = ({ + uiSettings, + query, + getInternalStartServices, +}: AggTypesDependencies) => ({ metrics: [ - countMetricAgg, - avgMetricAgg, - sumMetricAgg, - medianMetricAgg, - minMetricAgg, - maxMetricAgg, - stdDeviationMetricAgg, - cardinalityMetricAgg, - percentilesMetricAgg, - percentileRanksMetricAgg, - topHitMetricAgg, - derivativeMetricAgg, - cumulativeSumMetricAgg, - movingAvgMetricAgg, - serialDiffMetricAgg, - bucketAvgMetricAgg, - bucketSumMetricAgg, - bucketMinMetricAgg, - bucketMaxMetricAgg, - geoBoundsMetricAgg, - geoCentroidMetricAgg, + getCountMetricAgg({ getInternalStartServices }), + getAvgMetricAgg({ getInternalStartServices }), + getSumMetricAgg({ getInternalStartServices }), + getMedianMetricAgg({ getInternalStartServices }), + getMinMetricAgg({ getInternalStartServices }), + getMaxMetricAgg({ getInternalStartServices }), + getStdDeviationMetricAgg({ getInternalStartServices }), + getCardinalityMetricAgg({ getInternalStartServices }), + getPercentilesMetricAgg({ getInternalStartServices }), + getPercentileRanksMetricAgg({ getInternalStartServices }), + getTopHitMetricAgg({ getInternalStartServices }), + getDerivativeMetricAgg({ getInternalStartServices }), + getCumulativeSumMetricAgg({ getInternalStartServices }), + getMovingAvgMetricAgg({ getInternalStartServices }), + getSerialDiffMetricAgg({ getInternalStartServices }), + getBucketAvgMetricAgg({ getInternalStartServices }), + getBucketSumMetricAgg({ getInternalStartServices }), + getBucketMinMetricAgg({ getInternalStartServices }), + getBucketMaxMetricAgg({ getInternalStartServices }), + getGeoBoundsMetricAgg({ getInternalStartServices }), + getGeoCentroidMetricAgg({ getInternalStartServices }), ], buckets: [ - getDateHistogramBucketAgg({ uiSettings, query }), - getHistogramBucketAgg({ uiSettings, notifications }), - rangeBucketAgg, - getDateRangeBucketAgg({ uiSettings }), - ipRangeBucketAgg, - termsBucketAgg, - filterBucketAgg, - getFiltersBucketAgg({ uiSettings }), - significantTermsBucketAgg, - geoHashBucketAgg, - geoTileBucketAgg, + getDateHistogramBucketAgg({ uiSettings, query, getInternalStartServices }), + getHistogramBucketAgg({ uiSettings, getInternalStartServices }), + getRangeBucketAgg({ getInternalStartServices }), + getDateRangeBucketAgg({ uiSettings, getInternalStartServices }), + getIpRangeBucketAgg({ getInternalStartServices }), + getTermsBucketAgg({ getInternalStartServices }), + getFilterBucketAgg({ getInternalStartServices }), + getFiltersBucketAgg({ uiSettings, getInternalStartServices }), + getSignificantTermsBucketAgg({ getInternalStartServices }), + getGeoHashBucketAgg({ getInternalStartServices }), + getGeoTitleBucketAgg({ getInternalStartServices }), ], }); diff --git a/src/plugins/data/public/search/aggs/agg_types_registry.test.ts b/src/plugins/data/public/search/aggs/agg_types_registry.test.ts index 405f83e237de83..58d1a07d965e26 100644 --- a/src/plugins/data/public/search/aggs/agg_types_registry.test.ts +++ b/src/plugins/data/public/search/aggs/agg_types_registry.test.ts @@ -22,7 +22,7 @@ import { AggTypesRegistrySetup, AggTypesRegistryStart, } from './agg_types_registry'; -import { BucketAggType } from './buckets/_bucket_agg_type'; +import { BucketAggType } from './buckets/bucket_agg_type'; import { MetricAggType } from './metrics/metric_agg_type'; const bucketType = { name: 'terms', type: 'bucket' } as BucketAggType; diff --git a/src/plugins/data/public/search/aggs/agg_types_registry.ts b/src/plugins/data/public/search/aggs/agg_types_registry.ts index 8a8746106ae587..5a0c58120d810d 100644 --- a/src/plugins/data/public/search/aggs/agg_types_registry.ts +++ b/src/plugins/data/public/search/aggs/agg_types_registry.ts @@ -17,7 +17,7 @@ * under the License. */ -import { BucketAggType } from './buckets/_bucket_agg_type'; +import { BucketAggType } from './buckets/bucket_agg_type'; import { MetricAggType } from './metrics/metric_agg_type'; export type AggTypesRegistrySetup = ReturnType; diff --git a/src/plugins/data/public/search/aggs/buckets/_interval_options.ts b/src/plugins/data/public/search/aggs/buckets/_interval_options.ts index 393d3b745250f4..1c4c04c40a5c12 100644 --- a/src/plugins/data/public/search/aggs/buckets/_interval_options.ts +++ b/src/plugins/data/public/search/aggs/buckets/_interval_options.ts @@ -18,7 +18,7 @@ */ import { i18n } from '@kbn/i18n'; -import { IBucketAggConfig } from './_bucket_agg_type'; +import { IBucketAggConfig } from './bucket_agg_type'; export const intervalOptions = [ { diff --git a/src/plugins/data/public/search/aggs/buckets/_terms_other_bucket_helper.test.ts b/src/plugins/data/public/search/aggs/buckets/_terms_other_bucket_helper.test.ts index 9e4b93035384f6..c664325a168b17 100644 --- a/src/plugins/data/public/search/aggs/buckets/_terms_other_bucket_helper.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/_terms_other_bucket_helper.test.ts @@ -24,8 +24,8 @@ import { } from './_terms_other_bucket_helper'; import { AggConfigs, CreateAggConfigParams } from '../agg_configs'; import { BUCKET_TYPES } from './bucket_agg_types'; -import { IBucketAggConfig } from './_bucket_agg_type'; -import { mockDataServices, mockAggTypesRegistry } from '../test_helpers'; +import { IBucketAggConfig } from './bucket_agg_type'; +import { mockAggTypesRegistry } from '../test_helpers'; const indexPattern = { id: '1234', @@ -223,10 +223,6 @@ describe('Terms Agg Other bucket helper', () => { return new AggConfigs(indexPattern, [...aggs], { typesRegistry }); }; - beforeEach(() => { - mockDataServices(); - }); - describe('buildOtherBucketAgg', () => { test('returns a function', () => { const aggConfigs = getAggConfigs(singleTerm.aggs); diff --git a/src/plugins/data/public/search/aggs/buckets/_terms_other_bucket_helper.ts b/src/plugins/data/public/search/aggs/buckets/_terms_other_bucket_helper.ts index 4fd988e7b7e663..abda6b5fc5980d 100644 --- a/src/plugins/data/public/search/aggs/buckets/_terms_other_bucket_helper.ts +++ b/src/plugins/data/public/search/aggs/buckets/_terms_other_bucket_helper.ts @@ -21,7 +21,7 @@ import { isNumber, keys, values, find, each, cloneDeep, flatten } from 'lodash'; import { buildExistsFilter, buildPhrasesFilter, buildQueryFromFilters } from '../../../../common'; import { AggGroupNames } from '../agg_groups'; import { IAggConfigs } from '../agg_configs'; -import { IBucketAggConfig } from './_bucket_agg_type'; +import { IBucketAggConfig } from './bucket_agg_type'; /** * walks the aggregation DSL and returns DSL starting at aggregation with id of startFromAggId diff --git a/src/plugins/data/public/search/aggs/buckets/_bucket_agg_type.ts b/src/plugins/data/public/search/aggs/buckets/bucket_agg_type.ts similarity index 86% rename from src/plugins/data/public/search/aggs/buckets/_bucket_agg_type.ts rename to src/plugins/data/public/search/aggs/buckets/bucket_agg_type.ts index 03629c3189cbb4..f3c95b444dee90 100644 --- a/src/plugins/data/public/search/aggs/buckets/_bucket_agg_type.ts +++ b/src/plugins/data/public/search/aggs/buckets/bucket_agg_type.ts @@ -21,6 +21,7 @@ import { IAggConfig } from '../agg_config'; import { KBN_FIELD_TYPES } from '../../../../common'; import { AggType, AggTypeConfig } from '../agg_type'; import { AggParamType } from '../param_types/agg'; +import { GetInternalStartServicesFn } from '../../../types'; export interface IBucketAggConfig extends IAggConfig { type: InstanceType; @@ -39,6 +40,10 @@ interface BucketAggTypeConfig getKey?: (bucket: any, key: any, agg: IAggConfig) => any; } +interface BucketAggTypeDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} + export class BucketAggType extends AggType< TBucketAggConfig, BucketAggParam @@ -46,8 +51,11 @@ export class BucketAggType any; type = bucketType; - constructor(config: BucketAggTypeConfig) { - super(config); + constructor( + config: BucketAggTypeConfig, + dependencies: BucketAggTypeDependencies + ) { + super(config, dependencies); this.getKey = config.getKey || diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/date_histogram.test.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/date_histogram.test.ts index def354c4557cb1..97c940b4ff4b16 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/date_histogram.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/date_histogram.test.ts @@ -29,8 +29,9 @@ import { } from '../date_histogram'; import { BUCKET_TYPES } from '../bucket_agg_types'; import { RangeFilter } from '../../../../../common'; -import { coreMock } from '../../../../../../../core/public/mocks'; +import { coreMock, notificationServiceMock } from '../../../../../../../core/public/mocks'; import { queryServiceMock } from '../../../../query/mocks'; +import { fieldFormatsServiceMock } from '../../../../field_formats/mocks'; describe('AggConfig Filters', () => { describe('date_histogram', () => { @@ -46,6 +47,10 @@ describe('AggConfig Filters', () => { aggTypesDependencies = { uiSettings, query: queryServiceMock.createSetupContract(), + getInternalStartServices: () => ({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + }), }; mockDataServices(); @@ -90,7 +95,7 @@ describe('AggConfig Filters', () => { filter = createFilterDateHistogram(agg, bucketKey); }; - it('creates a valid range filter', () => { + test('creates a valid range filter', () => { init(); expect(filter).toHaveProperty('range'); @@ -110,7 +115,7 @@ describe('AggConfig Filters', () => { expect(filter.meta).toHaveProperty('index', '1234'); }); - it('extends the filter edge to 1ms before the next bucket for all interval options', () => { + test('extends the filter edge to 1ms before the next bucket for all interval options', () => { intervalOptions.forEach(option => { let duration; if (option.val !== 'custom' && moment(1, option.val).isValid()) { diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts index 6a03176959a838..8c0466b769a7e0 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.test.ts @@ -25,8 +25,9 @@ import { DateFormat } from '../../../../field_formats'; import { AggConfigs } from '../../agg_configs'; import { mockAggTypesRegistry } from '../../test_helpers'; import { BUCKET_TYPES } from '../bucket_agg_types'; -import { IBucketAggConfig } from '../_bucket_agg_type'; -import { coreMock } from '../../../../../../../core/public/mocks'; +import { IBucketAggConfig } from '../bucket_agg_type'; +import { coreMock, notificationServiceMock } from '../../../../../../../core/public/mocks'; +import { fieldFormatsServiceMock } from '../../../../field_formats/mocks'; describe('AggConfig Filters', () => { describe('Date range', () => { @@ -37,6 +38,10 @@ describe('AggConfig Filters', () => { aggTypesDependencies = { uiSettings, + getInternalStartServices: () => ({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + }), }; }); @@ -71,7 +76,7 @@ describe('AggConfig Filters', () => { ); }; - it('should return a range filter for date_range agg', () => { + test('should return a range filter for date_range agg', () => { const aggConfigs = getAggConfigs(); const from = new Date('1 Feb 2015'); const to = new Date('7 Feb 2015'); diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.ts index 9bfded0ce9729f..118e9b26e87d5b 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/date_range.ts @@ -18,7 +18,7 @@ */ import moment from 'moment'; -import { IBucketAggConfig } from '../_bucket_agg_type'; +import { IBucketAggConfig } from '../bucket_agg_type'; import { DateRangeKey } from '../lib/date_range'; import { buildRangeFilter, RangeFilterParams } from '../../../../../common'; diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/filters.test.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/filters.test.ts index 32ada8d57c7682..f5a0b5a7b90940 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/filters.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/filters.test.ts @@ -21,8 +21,9 @@ import { getFiltersBucketAgg, FiltersBucketAggDependencies } from '../filters'; import { createFilterFilters } from './filters'; import { AggConfigs } from '../../agg_configs'; import { mockAggTypesRegistry } from '../../test_helpers'; -import { IBucketAggConfig } from '../_bucket_agg_type'; -import { coreMock } from '../../../../../../../core/public/mocks'; +import { IBucketAggConfig } from '../bucket_agg_type'; +import { coreMock, notificationServiceMock } from '../../../../../../../core/public/mocks'; +import { fieldFormatsServiceMock } from '../../../../field_formats/mocks'; describe('AggConfig Filters', () => { describe('filters', () => { @@ -33,6 +34,10 @@ describe('AggConfig Filters', () => { aggTypesDependencies = { uiSettings, + getInternalStartServices: () => ({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + }), }; }); @@ -67,7 +72,8 @@ describe('AggConfig Filters', () => { { typesRegistry: mockAggTypesRegistry([getFiltersBucketAgg(aggTypesDependencies)]) } ); }; - it('should return a filters filter', () => { + + test('should return a filters filter', () => { const aggConfigs = getAggConfigs(); const filter = createFilterFilters(aggConfigs.aggs[0] as IBucketAggConfig, 'type:nginx'); diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/filters.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/filters.ts index 3b568d805f7c0d..1999b759a23d0f 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/filters.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/filters.ts @@ -18,7 +18,7 @@ */ import { get } from 'lodash'; -import { IBucketAggConfig } from '../_bucket_agg_type'; +import { IBucketAggConfig } from '../bucket_agg_type'; import { buildQueryFilter } from '../../../../../common'; export const createFilterFilters = (aggConfig: IBucketAggConfig, key: string) => { diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/histogram.test.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/histogram.test.ts index dc8414d80c0243..18b388be748773 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/histogram.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/histogram.test.ts @@ -19,19 +19,13 @@ import { createFilterHistogram } from './histogram'; import { AggConfigs } from '../../agg_configs'; -import { mockDataServices, mockAggTypesRegistry } from '../../test_helpers'; +import { mockAggTypesRegistry } from '../../test_helpers'; import { BUCKET_TYPES } from '../bucket_agg_types'; -import { IBucketAggConfig } from '../_bucket_agg_type'; +import { IBucketAggConfig } from '../bucket_agg_type'; import { BytesFormat, FieldFormatsGetConfigFn } from '../../../../../common'; describe('AggConfig Filters', () => { describe('histogram', () => { - beforeEach(() => { - mockDataServices(); - }); - - const typesRegistry = mockAggTypesRegistry(); - const getConfig = (() => {}) as FieldFormatsGetConfigFn; const getAggConfigs = () => { const field = { @@ -61,11 +55,11 @@ describe('AggConfig Filters', () => { }, }, ], - { typesRegistry } + { typesRegistry: mockAggTypesRegistry() } ); }; - it('should return an range filter for histogram', () => { + test('should return an range filter for histogram', () => { const aggConfigs = getAggConfigs(); const filter = createFilterHistogram(aggConfigs.aggs[0] as IBucketAggConfig, '2048'); diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/histogram.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/histogram.ts index d4c00a0991fe2c..f8e7747d49147c 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/histogram.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/histogram.ts @@ -17,7 +17,7 @@ * under the License. */ -import { IBucketAggConfig } from '../_bucket_agg_type'; +import { IBucketAggConfig } from '../bucket_agg_type'; import { buildRangeFilter, RangeFilterParams } from '../../../../../common'; export const createFilterHistogram = (aggConfig: IBucketAggConfig, key: string) => { diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/ip_range.test.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/ip_range.test.ts index ca51094da2f58a..b528313b080d03 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/ip_range.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/ip_range.test.ts @@ -17,17 +17,26 @@ * under the License. */ -import { ipRangeBucketAgg } from '../ip_range'; +import { getIpRangeBucketAgg } from '../ip_range'; import { createFilterIpRange } from './ip_range'; import { AggConfigs, CreateAggConfigParams } from '../../agg_configs'; import { mockAggTypesRegistry } from '../../test_helpers'; import { IpFormat } from '../../../../../common'; import { BUCKET_TYPES } from '../bucket_agg_types'; -import { IBucketAggConfig } from '../_bucket_agg_type'; +import { IBucketAggConfig } from '../bucket_agg_type'; +import { fieldFormatsServiceMock } from '../../../../field_formats/mocks'; +import { notificationServiceMock } from '../../../../../../../core/public/mocks'; describe('AggConfig Filters', () => { describe('IP range', () => { - const typesRegistry = mockAggTypesRegistry([ipRangeBucketAgg]); + const typesRegistry = mockAggTypesRegistry([ + getIpRangeBucketAgg({ + getInternalStartServices: () => ({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + }), + }), + ]); const getAggConfigs = (aggs: CreateAggConfigParams[]) => { const field = { name: 'ip', @@ -46,7 +55,7 @@ describe('AggConfig Filters', () => { return new AggConfigs(indexPattern, aggs, { typesRegistry }); }; - it('should return a range filter for ip_range agg', () => { + test('should return a range filter for ip_range agg', () => { const aggConfigs = getAggConfigs([ { type: BUCKET_TYPES.IP_RANGE, @@ -75,7 +84,7 @@ describe('AggConfig Filters', () => { expect(filter.range.ip).toHaveProperty('lte', '1.1.1.1'); }); - it('should return a range filter for ip_range agg using a CIDR mask', () => { + test('should return a range filter for ip_range agg using a CIDR mask', () => { const aggConfigs = getAggConfigs([ { type: BUCKET_TYPES.IP_RANGE, diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/ip_range.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/ip_range.ts index 2d34c45aaab9da..aae212783b8731 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/ip_range.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/ip_range.ts @@ -18,7 +18,7 @@ */ import { CidrMask } from '../lib/cidr_mask'; -import { IBucketAggConfig } from '../_bucket_agg_type'; +import { IBucketAggConfig } from '../bucket_agg_type'; import { IpRangeKey } from '../lib/ip_range'; import { buildRangeFilter, RangeFilterParams } from '../../../../../common'; diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/range.test.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/range.test.ts index 3a6f8b36a9d964..14a7538aa95a47 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/range.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/range.test.ts @@ -17,22 +17,31 @@ * under the License. */ -import { rangeBucketAgg } from '../range'; +import { getRangeBucketAgg, RangeBucketAggDependencies } from '../range'; import { createFilterRange } from './range'; import { BytesFormat, FieldFormatsGetConfigFn } from '../../../../../common'; import { AggConfigs } from '../../agg_configs'; import { mockDataServices, mockAggTypesRegistry } from '../../test_helpers'; import { BUCKET_TYPES } from '../bucket_agg_types'; -import { IBucketAggConfig } from '../_bucket_agg_type'; +import { IBucketAggConfig } from '../bucket_agg_type'; +import { fieldFormatsServiceMock } from '../../../../field_formats/mocks'; +import { notificationServiceMock } from '../../../../../../../core/public/mocks'; describe('AggConfig Filters', () => { describe('range', () => { + let aggTypesDependencies: RangeBucketAggDependencies; + beforeEach(() => { + aggTypesDependencies = { + getInternalStartServices: () => ({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + }), + }; + mockDataServices(); }); - const typesRegistry = mockAggTypesRegistry([rangeBucketAgg]); - const getConfig = (() => {}) as FieldFormatsGetConfigFn; const getAggConfigs = () => { const field = { @@ -62,11 +71,11 @@ describe('AggConfig Filters', () => { }, }, ], - { typesRegistry } + { typesRegistry: mockAggTypesRegistry([getRangeBucketAgg(aggTypesDependencies)]) } ); }; - it('should return a range filter for range agg', () => { + test('should return a range filter for range agg', () => { const aggConfigs = getAggConfigs(); const filter = createFilterRange(aggConfigs.aggs[0] as IBucketAggConfig, { gte: 1024, diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/range.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/range.ts index d3d85f2441a8ba..cbad8742bfab60 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/range.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/range.ts @@ -17,7 +17,7 @@ * under the License. */ -import { IBucketAggConfig } from '../_bucket_agg_type'; +import { IBucketAggConfig } from '../bucket_agg_type'; import { buildRangeFilter } from '../../../../../common'; export const createFilterRange = (aggConfig: IBucketAggConfig, params: any) => { diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/terms.test.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/terms.test.ts index 511af450b0113a..c11a7d1a4e6b81 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/terms.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/terms.test.ts @@ -17,17 +17,30 @@ * under the License. */ -import { termsBucketAgg } from '../terms'; +import { getTermsBucketAgg } from '../terms'; import { createFilterTerms } from './terms'; import { AggConfigs, CreateAggConfigParams } from '../../agg_configs'; import { mockAggTypesRegistry } from '../../test_helpers'; import { BUCKET_TYPES } from '../bucket_agg_types'; -import { IBucketAggConfig } from '../_bucket_agg_type'; +import { IBucketAggConfig } from '../bucket_agg_type'; import { Filter, ExistsFilter } from '../../../../../common'; +import { RangeBucketAggDependencies } from '../range'; +import { fieldFormatsServiceMock } from '../../../../field_formats/mocks'; +import { notificationServiceMock } from '../../../../../../../core/public/mocks'; describe('AggConfig Filters', () => { describe('terms', () => { - const typesRegistry = mockAggTypesRegistry([termsBucketAgg]); + let aggTypesDependencies: RangeBucketAggDependencies; + + beforeEach(() => { + aggTypesDependencies = { + getInternalStartServices: () => ({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + }), + }; + }); + const getAggConfigs = (aggs: CreateAggConfigParams[]) => { const indexPattern = { id: '1234', @@ -43,10 +56,12 @@ describe('AggConfig Filters', () => { indexPattern, }; - return new AggConfigs(indexPattern, aggs, { typesRegistry }); + return new AggConfigs(indexPattern, aggs, { + typesRegistry: mockAggTypesRegistry([getTermsBucketAgg(aggTypesDependencies)]), + }); }; - it('should return a match_phrase filter for terms', () => { + test('should return a match_phrase filter for terms', () => { const aggConfigs = getAggConfigs([ { type: BUCKET_TYPES.TERMS, schema: 'segment', params: { field: 'field' } }, ]); @@ -65,7 +80,7 @@ describe('AggConfig Filters', () => { expect(filter.meta).toHaveProperty('index', '1234'); }); - it('should set query to true or false for boolean filter', () => { + test('should set query to true or false for boolean filter', () => { const aggConfigs = getAggConfigs([ { type: BUCKET_TYPES.TERMS, schema: 'segment', params: { field: 'field' } }, ]); @@ -93,7 +108,7 @@ describe('AggConfig Filters', () => { expect(filterTrue.query.match_phrase.field).toBeTruthy(); }); - it('should generate correct __missing__ filter', () => { + test('should generate correct __missing__ filter', () => { const aggConfigs = getAggConfigs([ { type: BUCKET_TYPES.TERMS, schema: 'segment', params: { field: 'field' } }, ]); @@ -110,7 +125,7 @@ describe('AggConfig Filters', () => { expect(filter.meta).toHaveProperty('negate', true); }); - it('should generate correct __other__ filter', () => { + test('should generate correct __other__ filter', () => { const aggConfigs = getAggConfigs([ { type: BUCKET_TYPES.TERMS, schema: 'segment', params: { field: 'field' } }, ]); diff --git a/src/plugins/data/public/search/aggs/buckets/create_filter/terms.ts b/src/plugins/data/public/search/aggs/buckets/create_filter/terms.ts index 43ebfc0e90db2b..95de19b96abd4f 100644 --- a/src/plugins/data/public/search/aggs/buckets/create_filter/terms.ts +++ b/src/plugins/data/public/search/aggs/buckets/create_filter/terms.ts @@ -17,7 +17,7 @@ * under the License. */ -import { IBucketAggConfig } from '../_bucket_agg_type'; +import { IBucketAggConfig } from '../bucket_agg_type'; import { buildPhrasesFilter, buildExistsFilter, 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 7701f1bbcb4d0a..e6fd259fabc926 100644 --- a/src/plugins/data/public/search/aggs/buckets/date_histogram.ts +++ b/src/plugins/data/public/search/aggs/buckets/date_histogram.ts @@ -23,7 +23,7 @@ import { i18n } from '@kbn/i18n'; import { IUiSettingsClient } from 'src/core/public'; import { TimeBuckets } from './lib/time_buckets'; -import { BucketAggType, IBucketAggConfig } from './_bucket_agg_type'; +import { BucketAggType, IBucketAggConfig } from './bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; import { createFilterDateHistogram } from './create_filter/date_histogram'; import { intervalOptions } from './_interval_options'; @@ -33,8 +33,8 @@ import { isMetricAggType } from '../metrics/metric_agg_type'; import { FIELD_FORMAT_IDS, KBN_FIELD_TYPES } from '../../../../common'; import { TimefilterContract } from '../../../query'; -import { getFieldFormats } from '../../../../public/services'; import { QuerySetup } from '../../../query/query_service'; +import { GetInternalStartServicesFn } from '../../../types'; const detectedTimezone = moment.tz.guess(); const tzOffset = moment().format('Z'); @@ -61,6 +61,7 @@ interface ITimeBuckets { export interface DateHistogramBucketAggDependencies { uiSettings: IUiSettingsClient; query: QuerySetup; + getInternalStartServices: GetInternalStartServicesFn; } export interface IBucketDateHistogramAggConfig extends IBucketAggConfig { @@ -74,211 +75,218 @@ export function isDateHistogramBucketAggConfig(agg: any): agg is IBucketDateHist export const getDateHistogramBucketAgg = ({ uiSettings, query, + getInternalStartServices, }: DateHistogramBucketAggDependencies) => - new BucketAggType({ - name: BUCKET_TYPES.DATE_HISTOGRAM, - title: i18n.translate('data.search.aggs.buckets.dateHistogramTitle', { - defaultMessage: 'Date Histogram', - }), - ordered: { - date: true, - }, - makeLabel(agg) { - let output: Record = {}; + new BucketAggType( + { + name: BUCKET_TYPES.DATE_HISTOGRAM, + title: i18n.translate('data.search.aggs.buckets.dateHistogramTitle', { + defaultMessage: 'Date Histogram', + }), + ordered: { + date: true, + }, + makeLabel(agg) { + let output: Record = {}; - if (this.params) { - output = writeParams(this.params, agg); - } + if (this.params) { + output = writeParams(this.params, agg); + } - const field = agg.getFieldDisplayName(); - return i18n.translate('data.search.aggs.buckets.dateHistogramLabel', { - defaultMessage: '{fieldName} per {intervalDescription}', - values: { - fieldName: field, - intervalDescription: output.metricScaleText || output.bucketInterval.description, - }, - }); - }, - createFilter: createFilterDateHistogram, - decorateAggConfig() { - let buckets: any; + const field = agg.getFieldDisplayName(); + return i18n.translate('data.search.aggs.buckets.dateHistogramLabel', { + defaultMessage: '{fieldName} per {intervalDescription}', + values: { + fieldName: field, + intervalDescription: output.metricScaleText || output.bucketInterval.description, + }, + }); + }, + createFilter: createFilterDateHistogram, + decorateAggConfig() { + let buckets: any; - return { - buckets: { - configurable: true, - get() { - if (buckets) return buckets; + return { + buckets: { + configurable: true, + get() { + if (buckets) return buckets; - const { timefilter } = query.timefilter; - buckets = new TimeBuckets({ uiSettings }); - updateTimeBuckets(this, timefilter, buckets); + const { timefilter } = query.timefilter; + buckets = new TimeBuckets({ uiSettings }); + updateTimeBuckets(this, timefilter, buckets); - return buckets; - }, - } as any, - }; - }, - getFormat(agg) { - const DateFieldFormat = getFieldFormats().getType(FIELD_FORMAT_IDS.DATE); + return buckets; + }, + } as any, + }; + }, + getFormat(agg) { + const { fieldFormats } = getInternalStartServices(); + const DateFieldFormat = fieldFormats.getType(FIELD_FORMAT_IDS.DATE); - if (!DateFieldFormat) { - throw new Error('Unable to retrieve Date Field Format'); - } + if (!DateFieldFormat) { + throw new Error('Unable to retrieve Date Field Format'); + } - return new DateFieldFormat( + return new DateFieldFormat( + { + pattern: agg.buckets.getScaledDateFormat(), + }, + (key: string) => uiSettings.get(key) + ); + }, + params: [ { - pattern: agg.buckets.getScaledDateFormat(), + name: 'field', + type: 'field', + filterFieldTypes: KBN_FIELD_TYPES.DATE, + default(agg: IBucketDateHistogramAggConfig) { + return agg.getIndexPattern().timeFieldName; + }, + onChange(agg: IBucketDateHistogramAggConfig) { + if (get(agg, 'params.interval') === 'auto' && !agg.fieldIsTimeField()) { + delete agg.params.interval; + } + }, }, - (key: string) => uiSettings.get(key) - ); - }, - params: [ - { - name: 'field', - type: 'field', - filterFieldTypes: KBN_FIELD_TYPES.DATE, - default(agg: IBucketDateHistogramAggConfig) { - return agg.getIndexPattern().timeFieldName; + { + name: 'timeRange', + default: null, + write: noop, }, - onChange(agg: IBucketDateHistogramAggConfig) { - if (get(agg, 'params.interval') === 'auto' && !agg.fieldIsTimeField()) { - delete agg.params.interval; - } + { + name: 'useNormalizedEsInterval', + default: true, + write: noop, }, - }, - { - name: 'timeRange', - default: null, - write: noop, - }, - { - name: 'useNormalizedEsInterval', - default: true, - write: noop, - }, - { - name: 'scaleMetricValues', - default: false, - write: noop, - advanced: true, - }, - { - name: 'interval', - deserialize(state: any, agg) { - // For upgrading from 7.0.x to 7.1.x - intervals are now stored as key of options or custom value - if (state === 'custom') { - return get(agg, 'params.customInterval'); - } + { + name: 'scaleMetricValues', + default: false, + write: noop, + advanced: true, + }, + { + name: 'interval', + deserialize(state: any, agg) { + // For upgrading from 7.0.x to 7.1.x - intervals are now stored as key of options or custom value + if (state === 'custom') { + return get(agg, 'params.customInterval'); + } - const interval = find(intervalOptions, { val: state }); + const interval = find(intervalOptions, { val: state }); - // For upgrading from 4.0.x to 4.1.x - intervals are now stored as 'y' instead of 'year', - // but this maps the old values to the new values - if (!interval && state === 'year') { - return 'y'; - } - return state; - }, - default: 'auto', - options: intervalOptions, - write(agg, output, aggs) { - const { timefilter } = query.timefilter; - updateTimeBuckets(agg, timefilter); + // For upgrading from 4.0.x to 4.1.x - intervals are now stored as 'y' instead of 'year', + // but this maps the old values to the new values + if (!interval && state === 'year') { + return 'y'; + } + return state; + }, + default: 'auto', + options: intervalOptions, + write(agg, output, aggs) { + const { timefilter } = query.timefilter; + updateTimeBuckets(agg, timefilter); - const { useNormalizedEsInterval, scaleMetricValues } = agg.params; - const interval = agg.buckets.getInterval(useNormalizedEsInterval); - output.bucketInterval = interval; - if (interval.expression === '0ms') { - // We are hitting this code a couple of times while configuring in editor - // with an interval of 0ms because the overall time range has not yet been - // set. Since 0ms is not a valid ES interval, we cannot pass it through dateHistogramInterval - // below, since it would throw an exception. So in the cases we still have an interval of 0ms - // here we simply skip the rest of the method and never write an interval into the DSL, since - // this DSL will anyway not be used before we're passing this code with an actual interval. - return; - } - output.params = { - ...output.params, - ...dateHistogramInterval(interval.expression), - }; + const { useNormalizedEsInterval, scaleMetricValues } = agg.params; + const interval = agg.buckets.getInterval(useNormalizedEsInterval); + output.bucketInterval = interval; + if (interval.expression === '0ms') { + // We are hitting this code a couple of times while configuring in editor + // with an interval of 0ms because the overall time range has not yet been + // set. Since 0ms is not a valid ES interval, we cannot pass it through dateHistogramInterval + // below, since it would throw an exception. So in the cases we still have an interval of 0ms + // here we simply skip the rest of the method and never write an interval into the DSL, since + // this DSL will anyway not be used before we're passing this code with an actual interval. + return; + } + output.params = { + ...output.params, + ...dateHistogramInterval(interval.expression), + }; - const scaleMetrics = scaleMetricValues && interval.scaled && interval.scale < 1; - if (scaleMetrics && aggs) { - const metrics = aggs.aggs.filter(a => isMetricAggType(a.type)); - const all = every(metrics, (a: IBucketAggConfig) => { - const { type } = a; + const scaleMetrics = scaleMetricValues && interval.scaled && interval.scale < 1; + if (scaleMetrics && aggs) { + const metrics = aggs.aggs.filter(a => isMetricAggType(a.type)); + const all = every(metrics, (a: IBucketAggConfig) => { + const { type } = a; - if (isMetricAggType(type)) { - return type.isScalable(); + if (isMetricAggType(type)) { + return type.isScalable(); + } + }); + if (all) { + output.metricScale = interval.scale; + output.metricScaleText = interval.preScaled.description; } - }); - if (all) { - output.metricScale = interval.scale; - output.metricScaleText = interval.preScaled.description; } - } + }, }, - }, - { - name: 'time_zone', - default: undefined, - // We don't ever want this parameter to be serialized out (when saving or to URLs) - // since we do all the logic handling it "on the fly" in the `write` method, to prevent - // time_zones being persisted into saved_objects - serialize: noop, - write(agg, output) { - // If a time_zone has been set explicitly always prefer this. - let tz = agg.params.time_zone; - if (!tz && agg.params.field) { - // If a field has been configured check the index pattern's typeMeta if a date_histogram on that - // field requires a specific time_zone - tz = get(agg.getIndexPattern(), [ - 'typeMeta', - 'aggs', - 'date_histogram', - agg.params.field.name, - 'time_zone', - ]); - } - if (!tz) { - // If the index pattern typeMeta data, didn't had a time zone assigned for the selected field use the configured tz - const isDefaultTimezone = uiSettings.isDefault('dateFormat:tz'); - tz = isDefaultTimezone ? detectedTimezone || tzOffset : uiSettings.get('dateFormat:tz'); - } - output.params.time_zone = tz; + { + name: 'time_zone', + default: undefined, + // We don't ever want this parameter to be serialized out (when saving or to URLs) + // since we do all the logic handling it "on the fly" in the `write` method, to prevent + // time_zones being persisted into saved_objects + serialize: noop, + write(agg, output) { + // If a time_zone has been set explicitly always prefer this. + let tz = agg.params.time_zone; + if (!tz && agg.params.field) { + // If a field has been configured check the index pattern's typeMeta if a date_histogram on that + // field requires a specific time_zone + tz = get(agg.getIndexPattern(), [ + 'typeMeta', + 'aggs', + 'date_histogram', + agg.params.field.name, + 'time_zone', + ]); + } + if (!tz) { + // If the index pattern typeMeta data, didn't had a time zone assigned for the selected field use the configured tz + const isDefaultTimezone = uiSettings.isDefault('dateFormat:tz'); + tz = isDefaultTimezone + ? detectedTimezone || tzOffset + : uiSettings.get('dateFormat:tz'); + } + output.params.time_zone = tz; + }, }, - }, - { - name: 'drop_partials', - default: false, - write: noop, - shouldShow: agg => { - const field = agg.params.field; - return field && field.name && field.name === agg.getIndexPattern().timeFieldName; + { + name: 'drop_partials', + default: false, + write: noop, + shouldShow: agg => { + const field = agg.params.field; + return field && field.name && field.name === agg.getIndexPattern().timeFieldName; + }, }, - }, - { - name: 'format', - }, - { - name: 'min_doc_count', - default: 1, - }, - { - name: 'extended_bounds', - default: {}, - write(agg, output) { - const val = agg.params.extended_bounds; + { + name: 'format', + }, + { + name: 'min_doc_count', + default: 1, + }, + { + name: 'extended_bounds', + default: {}, + write(agg, output) { + const val = agg.params.extended_bounds; - if (val.min != null || val.max != null) { - output.params.extended_bounds = { - min: moment(val.min).valueOf(), - max: moment(val.max).valueOf(), - }; + if (val.min != null || val.max != null) { + output.params.extended_bounds = { + min: moment(val.min).valueOf(), + max: moment(val.max).valueOf(), + }; - return; - } + return; + } + }, }, - }, - ], - }); + ], + }, + { getInternalStartServices } + ); diff --git a/src/plugins/data/public/search/aggs/buckets/date_range.test.ts b/src/plugins/data/public/search/aggs/buckets/date_range.test.ts index 4ea550492fa09d..c050620c3a856f 100644 --- a/src/plugins/data/public/search/aggs/buckets/date_range.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/date_range.test.ts @@ -17,11 +17,12 @@ * under the License. */ -import { coreMock } from '../../../../../../../src/core/public/mocks'; +import { coreMock, notificationServiceMock } from '../../../../../../../src/core/public/mocks'; import { getDateRangeBucketAgg, DateRangeBucketAggDependencies } from './date_range'; import { AggConfigs } from '../agg_configs'; import { mockAggTypesRegistry } from '../test_helpers'; import { BUCKET_TYPES } from './bucket_agg_types'; +import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; describe('date_range params', () => { let aggTypesDependencies: DateRangeBucketAggDependencies; @@ -31,6 +32,10 @@ describe('date_range params', () => { aggTypesDependencies = { uiSettings, + getInternalStartServices: () => ({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + }), }; }); diff --git a/src/plugins/data/public/search/aggs/buckets/date_range.ts b/src/plugins/data/public/search/aggs/buckets/date_range.ts index 8133a47ec7248d..07d927e64a943d 100644 --- a/src/plugins/data/public/search/aggs/buckets/date_range.ts +++ b/src/plugins/data/public/search/aggs/buckets/date_range.ts @@ -23,12 +23,12 @@ import { i18n } from '@kbn/i18n'; import { IUiSettingsClient } from 'src/core/public'; import { BUCKET_TYPES } from './bucket_agg_types'; -import { BucketAggType, IBucketAggConfig } from './_bucket_agg_type'; +import { BucketAggType, IBucketAggConfig } from './bucket_agg_type'; import { createFilterDateRange } from './create_filter/date_range'; import { convertDateRangeToString, DateRangeKey } from './lib/date_range'; import { KBN_FIELD_TYPES, FieldFormat, TEXT_CONTEXT_TYPE } from '../../../../common'; -import { getFieldFormats } from '../../../../public/services'; +import { GetInternalStartServicesFn } from '../../../types'; const dateRangeTitle = i18n.translate('data.search.aggs.buckets.dateRangeTitle', { defaultMessage: 'Date Range', @@ -36,76 +36,85 @@ const dateRangeTitle = i18n.translate('data.search.aggs.buckets.dateRangeTitle', export interface DateRangeBucketAggDependencies { uiSettings: IUiSettingsClient; + getInternalStartServices: GetInternalStartServicesFn; } -export const getDateRangeBucketAgg = ({ uiSettings }: DateRangeBucketAggDependencies) => - new BucketAggType({ - name: BUCKET_TYPES.DATE_RANGE, - title: dateRangeTitle, - createFilter: createFilterDateRange, - getKey({ from, to }): DateRangeKey { - return { from, to }; - }, - getFormat(agg) { - const fieldFormatsService = getFieldFormats(); +export const getDateRangeBucketAgg = ({ + uiSettings, + getInternalStartServices, +}: DateRangeBucketAggDependencies) => + new BucketAggType( + { + name: BUCKET_TYPES.DATE_RANGE, + title: dateRangeTitle, + createFilter: createFilterDateRange, + getKey({ from, to }): DateRangeKey { + return { from, to }; + }, + getFormat(agg) { + const { fieldFormats } = getInternalStartServices(); - const formatter = agg.fieldOwnFormatter( - TEXT_CONTEXT_TYPE, - fieldFormatsService.getDefaultInstance(KBN_FIELD_TYPES.DATE) - ); - const DateRangeFormat = FieldFormat.from(function(range: DateRangeKey) { - return convertDateRangeToString(range, formatter); - }); - return new DateRangeFormat(); - }, - makeLabel(aggConfig) { - return aggConfig.getFieldDisplayName() + ' date ranges'; - }, - params: [ - { - name: 'field', - type: 'field', - filterFieldTypes: KBN_FIELD_TYPES.DATE, - default(agg: IBucketAggConfig) { - return agg.getIndexPattern().timeFieldName; - }, + const formatter = agg.fieldOwnFormatter( + TEXT_CONTEXT_TYPE, + fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.DATE) + ); + const DateRangeFormat = FieldFormat.from(function(range: DateRangeKey) { + return convertDateRangeToString(range, formatter); + }); + return new DateRangeFormat(); }, - { - name: 'ranges', - default: [ - { - from: 'now-1w/w', - to: 'now', - }, - ], + makeLabel(aggConfig) { + return aggConfig.getFieldDisplayName() + ' date ranges'; }, - { - name: 'time_zone', - default: undefined, - // Implimentation method is the same as that of date_histogram - serialize: () => undefined, - write: (agg, output) => { - const field = agg.getParam('field'); - let tz = agg.getParam('time_zone'); + params: [ + { + name: 'field', + type: 'field', + filterFieldTypes: KBN_FIELD_TYPES.DATE, + default(agg: IBucketAggConfig) { + return agg.getIndexPattern().timeFieldName; + }, + }, + { + name: 'ranges', + default: [ + { + from: 'now-1w/w', + to: 'now', + }, + ], + }, + { + name: 'time_zone', + default: undefined, + // Implimentation method is the same as that of date_histogram + serialize: () => undefined, + write: (agg, output) => { + const field = agg.getParam('field'); + let tz = agg.getParam('time_zone'); - if (!tz && field) { - tz = get(agg.getIndexPattern(), [ - 'typeMeta', - 'aggs', - 'date_range', - field.name, - 'time_zone', - ]); - } - if (!tz) { - const detectedTimezone = moment.tz.guess(); - const tzOffset = moment().format('Z'); - const isDefaultTimezone = uiSettings.isDefault('dateFormat:tz'); + if (!tz && field) { + tz = get(agg.getIndexPattern(), [ + 'typeMeta', + 'aggs', + 'date_range', + field.name, + 'time_zone', + ]); + } + if (!tz) { + const detectedTimezone = moment.tz.guess(); + const tzOffset = moment().format('Z'); + const isDefaultTimezone = uiSettings.isDefault('dateFormat:tz'); - tz = isDefaultTimezone ? detectedTimezone || tzOffset : uiSettings.get('dateFormat:tz'); - } - output.params.time_zone = tz; + tz = isDefaultTimezone + ? detectedTimezone || tzOffset + : uiSettings.get('dateFormat:tz'); + } + output.params.time_zone = tz; + }, }, - }, - ], - }); + ], + }, + { getInternalStartServices } + ); diff --git a/src/plugins/data/public/search/aggs/buckets/filter.ts b/src/plugins/data/public/search/aggs/buckets/filter.ts index 80efc0cf92071d..accbdf4dd783d9 100644 --- a/src/plugins/data/public/search/aggs/buckets/filter.ts +++ b/src/plugins/data/public/search/aggs/buckets/filter.ts @@ -18,19 +18,28 @@ */ import { i18n } from '@kbn/i18n'; -import { BucketAggType } from './_bucket_agg_type'; +import { BucketAggType } from './bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; +import { GetInternalStartServicesFn } from '../../../types'; const filterTitle = i18n.translate('data.search.aggs.buckets.filterTitle', { defaultMessage: 'Filter', }); -export const filterBucketAgg = new BucketAggType({ - name: BUCKET_TYPES.FILTER, - title: filterTitle, - params: [ +export interface FilterBucketAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} + +export const getFilterBucketAgg = ({ getInternalStartServices }: FilterBucketAggDependencies) => + new BucketAggType( { - name: 'geo_bounding_box', + name: BUCKET_TYPES.FILTER, + title: filterTitle, + params: [ + { + name: 'geo_bounding_box', + }, + ], }, - ], -}); + { getInternalStartServices } + ); diff --git a/src/plugins/data/public/search/aggs/buckets/filters.ts b/src/plugins/data/public/search/aggs/buckets/filters.ts index 8b9aca87f8735c..a42cb70a62b7de 100644 --- a/src/plugins/data/public/search/aggs/buckets/filters.ts +++ b/src/plugins/data/public/search/aggs/buckets/filters.ts @@ -23,11 +23,12 @@ import { IUiSettingsClient } from 'src/core/public'; import { createFilterFilters } from './create_filter/filters'; import { toAngularJSON } from '../utils'; -import { BucketAggType } from './_bucket_agg_type'; +import { BucketAggType } from './bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; import { Storage } from '../../../../../../plugins/kibana_utils/public'; import { getEsQueryConfig, buildEsQuery, Query } from '../../../../common'; import { getQueryLog } from '../../../query'; +import { GetInternalStartServicesFn } from '../../../types'; const filtersTitle = i18n.translate('data.search.aggs.buckets.filtersTitle', { defaultMessage: 'Filters', @@ -43,69 +44,81 @@ interface FilterValue { export interface FiltersBucketAggDependencies { uiSettings: IUiSettingsClient; + getInternalStartServices: GetInternalStartServicesFn; } -export const getFiltersBucketAgg = ({ uiSettings }: FiltersBucketAggDependencies) => - new BucketAggType({ - name: BUCKET_TYPES.FILTERS, - title: filtersTitle, - createFilter: createFilterFilters, - customLabels: false, - params: [ - { - name: 'filters', - default: [ - { input: { query: '', language: uiSettings.get('search:queryLanguage') }, label: '' }, - ], - write(aggConfig, output) { - const inFilters: FilterValue[] = aggConfig.params.filters; - if (!size(inFilters)) return; +export const getFiltersBucketAgg = ({ + uiSettings, + getInternalStartServices, +}: FiltersBucketAggDependencies) => + new BucketAggType( + { + name: BUCKET_TYPES.FILTERS, + title: filtersTitle, + createFilter: createFilterFilters, + customLabels: false, + params: [ + { + name: 'filters', + default: [ + { input: { query: '', language: uiSettings.get('search:queryLanguage') }, label: '' }, + ], + write(aggConfig, output) { + const inFilters: FilterValue[] = aggConfig.params.filters; + if (!size(inFilters)) return; - inFilters.forEach(filter => { - const persistedLog = getQueryLog( - uiSettings, - new Storage(window.localStorage), - 'vis_default_editor', - filter.input.language - ); - persistedLog.add(filter.input.query); - }); + inFilters.forEach(filter => { + const persistedLog = getQueryLog( + uiSettings, + new Storage(window.localStorage), + 'vis_default_editor', + filter.input.language + ); + persistedLog.add(filter.input.query); + }); - const outFilters = transform( - inFilters, - function(filters, filter) { - const input = cloneDeep(filter.input); + const outFilters = transform( + inFilters, + function(filters, filter) { + const input = cloneDeep(filter.input); - if (!input) { - console.log('malformed filter agg params, missing "input" query'); // eslint-disable-line no-console - return; - } + if (!input) { + console.log('malformed filter agg params, missing "input" query'); // eslint-disable-line no-console + return; + } - const esQueryConfigs = getEsQueryConfig(uiSettings); - const query = buildEsQuery(aggConfig.getIndexPattern(), [input], [], esQueryConfigs); + const esQueryConfigs = getEsQueryConfig(uiSettings); + const query = buildEsQuery( + aggConfig.getIndexPattern(), + [input], + [], + esQueryConfigs + ); - if (!query) { - console.log('malformed filter agg params, missing "query" on input'); // eslint-disable-line no-console - return; - } + if (!query) { + console.log('malformed filter agg params, missing "query" on input'); // eslint-disable-line no-console + return; + } - const matchAllLabel = filter.input.query === '' ? '*' : ''; - const label = - filter.label || - matchAllLabel || - (typeof filter.input.query === 'string' - ? filter.input.query - : toAngularJSON(filter.input.query)); - filters[label] = { query }; - }, - {} - ); + const matchAllLabel = filter.input.query === '' ? '*' : ''; + const label = + filter.label || + matchAllLabel || + (typeof filter.input.query === 'string' + ? filter.input.query + : toAngularJSON(filter.input.query)); + filters[label] = { query }; + }, + {} + ); - if (!size(outFilters)) return; + if (!size(outFilters)) return; - const params = output.params || (output.params = {}); - params.filters = outFilters; + const params = output.params || (output.params = {}); + params.filters = outFilters; + }, }, - }, - ], - }); + ], + }, + { getInternalStartServices } + ); diff --git a/src/plugins/data/public/search/aggs/buckets/geo_hash.test.ts b/src/plugins/data/public/search/aggs/buckets/geo_hash.test.ts index 408cdf22bcbc22..24270dd33a5763 100644 --- a/src/plugins/data/public/search/aggs/buckets/geo_hash.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/geo_hash.test.ts @@ -17,13 +17,29 @@ * under the License. */ -import { geoHashBucketAgg } from './geo_hash'; +import { getGeoHashBucketAgg, GeoHashBucketAggDependencies } from './geo_hash'; import { AggConfigs, IAggConfigs } from '../agg_configs'; import { mockAggTypesRegistry } from '../test_helpers'; import { BUCKET_TYPES } from './bucket_agg_types'; -import { IBucketAggConfig } from './_bucket_agg_type'; +import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; +import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; +import { BucketAggType, IBucketAggConfig } from './bucket_agg_type'; describe('Geohash Agg', () => { + let aggTypesDependencies: GeoHashBucketAggDependencies; + let geoHashBucketAgg: BucketAggType; + + beforeEach(() => { + aggTypesDependencies = { + getInternalStartServices: () => ({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + }), + }; + + geoHashBucketAgg = getGeoHashBucketAgg(aggTypesDependencies); + }); + const getAggConfigs = (params?: Record) => { const indexPattern = { id: '1234', @@ -74,7 +90,7 @@ describe('Geohash Agg', () => { precisionParam = geoHashBucketAgg.params[PRECISION_PARAM_INDEX]; }); - it('should select precision parameter', () => { + test('should select precision parameter', () => { expect(precisionParam.name).toEqual('precision'); }); }); @@ -89,7 +105,7 @@ describe('Geohash Agg', () => { geoHashGridAgg = aggConfigs.aggs[0] as IBucketAggConfig; }); - it('should create filter, geohash_grid, and geo_centroid aggregations', () => { + test('should create filter, geohash_grid, and geo_centroid aggregations', () => { const requestAggs = geoHashBucketAgg.getRequestAggs(geoHashGridAgg) as IBucketAggConfig[]; expect(requestAggs.length).toEqual(3); @@ -101,7 +117,7 @@ describe('Geohash Agg', () => { }); describe('aggregation options', () => { - it('should only create geohash_grid and geo_centroid aggregations when isFilteredByCollar is false', () => { + test('should only create geohash_grid and geo_centroid aggregations when isFilteredByCollar is false', () => { const aggConfigs = getAggConfigs({ isFilteredByCollar: false }); const requestAggs = geoHashBucketAgg.getRequestAggs( aggConfigs.aggs[0] as IBucketAggConfig @@ -112,7 +128,7 @@ describe('Geohash Agg', () => { expect(requestAggs[1].type.name).toEqual('geo_centroid'); }); - it('should only create filter and geohash_grid aggregations when useGeocentroid is false', () => { + test('should only create filter and geohash_grid aggregations when useGeocentroid is false', () => { const aggConfigs = getAggConfigs({ useGeocentroid: false }); const requestAggs = geoHashBucketAgg.getRequestAggs( aggConfigs.aggs[0] as IBucketAggConfig @@ -138,7 +154,7 @@ describe('Geohash Agg', () => { ) as IBucketAggConfig[]; }); - it('should change geo_bounding_box filter aggregation and vis session state when map movement is outside map collar', () => { + test('should change geo_bounding_box filter aggregation and vis session state when map movement is outside map collar', () => { const [, geoBoxingBox] = geoHashBucketAgg.getRequestAggs( getAggConfigs({ boundingBox: { @@ -151,7 +167,7 @@ describe('Geohash Agg', () => { expect(originalRequestAggs[1].params).not.toEqual(geoBoxingBox.params); }); - it('should not change geo_bounding_box filter aggregation and vis session state when map movement is within map collar', () => { + test('should not change geo_bounding_box filter aggregation and vis session state when map movement is within map collar', () => { const [, geoBoxingBox] = geoHashBucketAgg.getRequestAggs( getAggConfigs({ boundingBox: { diff --git a/src/plugins/data/public/search/aggs/buckets/geo_hash.ts b/src/plugins/data/public/search/aggs/buckets/geo_hash.ts index 3ffec09a843878..eab10edad60f63 100644 --- a/src/plugins/data/public/search/aggs/buckets/geo_hash.ts +++ b/src/plugins/data/public/search/aggs/buckets/geo_hash.ts @@ -18,9 +18,10 @@ */ import { i18n } from '@kbn/i18n'; -import { BucketAggType, IBucketAggConfig } from './_bucket_agg_type'; +import { BucketAggType, IBucketAggConfig } from './bucket_agg_type'; import { KBN_FIELD_TYPES } from '../../../../common'; import { BUCKET_TYPES } from './bucket_agg_types'; +import { GetInternalStartServicesFn } from '../../../types'; const defaultBoundingBox = { top_left: { lat: 1, lon: 1 }, @@ -33,83 +34,91 @@ const geohashGridTitle = i18n.translate('data.search.aggs.buckets.geohashGridTit defaultMessage: 'Geohash', }); -export const geoHashBucketAgg = new BucketAggType({ - name: BUCKET_TYPES.GEOHASH_GRID, - title: geohashGridTitle, - params: [ - { - name: 'field', - type: 'field', - filterFieldTypes: KBN_FIELD_TYPES.GEO_POINT, - }, - { - name: 'autoPrecision', - default: true, - write: () => {}, - }, - { - name: 'precision', - default: defaultPrecision, - write(aggConfig, output) { - output.params.precision = aggConfig.params.precision; - }, - }, - { - name: 'useGeocentroid', - default: true, - write: () => {}, - }, - { - name: 'isFilteredByCollar', - default: true, - write: () => {}, - }, +export interface GeoHashBucketAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} + +export const getGeoHashBucketAgg = ({ getInternalStartServices }: GeoHashBucketAggDependencies) => + new BucketAggType( { - name: 'boundingBox', - default: null, - write: () => {}, - }, - ], - getRequestAggs(agg) { - const aggs = []; - const params = agg.params; + name: BUCKET_TYPES.GEOHASH_GRID, + title: geohashGridTitle, + params: [ + { + name: 'field', + type: 'field', + filterFieldTypes: KBN_FIELD_TYPES.GEO_POINT, + }, + { + name: 'autoPrecision', + default: true, + write: () => {}, + }, + { + name: 'precision', + default: defaultPrecision, + write(aggConfig, output) { + output.params.precision = aggConfig.params.precision; + }, + }, + { + name: 'useGeocentroid', + default: true, + write: () => {}, + }, + { + name: 'isFilteredByCollar', + default: true, + write: () => {}, + }, + { + name: 'boundingBox', + default: null, + write: () => {}, + }, + ], + getRequestAggs(agg) { + const aggs = []; + const params = agg.params; - if (params.isFilteredByCollar && agg.getField()) { - aggs.push( - agg.aggConfigs.createAggConfig( - { - type: 'filter', - id: 'filter_agg', - enabled: true, - params: { - geo_bounding_box: { - ignore_unmapped: true, - [agg.getField().name]: params.boundingBox || defaultBoundingBox, - }, - }, - } as any, - { addToAggConfigs: false } - ) - ); - } + if (params.isFilteredByCollar && agg.getField()) { + aggs.push( + agg.aggConfigs.createAggConfig( + { + type: 'filter', + id: 'filter_agg', + enabled: true, + params: { + geo_bounding_box: { + ignore_unmapped: true, + [agg.getField().name]: params.boundingBox || defaultBoundingBox, + }, + }, + } as any, + { addToAggConfigs: false } + ) + ); + } - aggs.push(agg); + aggs.push(agg); - if (params.useGeocentroid) { - aggs.push( - agg.aggConfigs.createAggConfig( - { - type: 'geo_centroid', - enabled: true, - params: { - field: agg.getField(), - }, - }, - { addToAggConfigs: false } - ) - ); - } + if (params.useGeocentroid) { + aggs.push( + agg.aggConfigs.createAggConfig( + { + type: 'geo_centroid', + enabled: true, + params: { + field: agg.getField(), + }, + }, + { addToAggConfigs: false } + ) + ); + } - return aggs; - }, -}); + return aggs; + }, + }, + { getInternalStartServices } + ); diff --git a/src/plugins/data/public/search/aggs/buckets/geo_tile.ts b/src/plugins/data/public/search/aggs/buckets/geo_tile.ts index 759601fc0c180c..c981e8400f9a12 100644 --- a/src/plugins/data/public/search/aggs/buckets/geo_tile.ts +++ b/src/plugins/data/public/search/aggs/buckets/geo_tile.ts @@ -20,53 +20,61 @@ import { i18n } from '@kbn/i18n'; import { noop } from 'lodash'; -import { BucketAggType } from './_bucket_agg_type'; +import { BucketAggType, IBucketAggConfig } from './bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; -import { IBucketAggConfig } from './_bucket_agg_type'; import { METRIC_TYPES } from '../metrics/metric_agg_types'; +import { GetInternalStartServicesFn } from '../../../types'; + +export interface GeoTitleBucketAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} const geotileGridTitle = i18n.translate('data.search.aggs.buckets.geotileGridTitle', { defaultMessage: 'Geotile', }); -export const geoTileBucketAgg = new BucketAggType({ - name: BUCKET_TYPES.GEOTILE_GRID, - title: geotileGridTitle, - params: [ - { - name: 'field', - type: 'field', - filterFieldTypes: KBN_FIELD_TYPES.GEO_POINT, - }, +export const getGeoTitleBucketAgg = ({ getInternalStartServices }: GeoTitleBucketAggDependencies) => + new BucketAggType( { - name: 'useGeocentroid', - default: true, - write: noop, - }, - { - name: 'precision', - default: 0, - }, - ], - getRequestAggs(agg) { - const aggs = []; - const useGeocentroid = agg.getParam('useGeocentroid'); + name: BUCKET_TYPES.GEOTILE_GRID, + title: geotileGridTitle, + params: [ + { + name: 'field', + type: 'field', + filterFieldTypes: KBN_FIELD_TYPES.GEO_POINT, + }, + { + name: 'useGeocentroid', + default: true, + write: noop, + }, + { + name: 'precision', + default: 0, + }, + ], + getRequestAggs(agg) { + const aggs = []; + const useGeocentroid = agg.getParam('useGeocentroid'); - aggs.push(agg); + aggs.push(agg); - if (useGeocentroid) { - const aggConfig = { - type: METRIC_TYPES.GEO_CENTROID, - enabled: true, - params: { - field: agg.getField(), - }, - }; + if (useGeocentroid) { + const aggConfig = { + type: METRIC_TYPES.GEO_CENTROID, + enabled: true, + params: { + field: agg.getField(), + }, + }; - aggs.push(agg.aggConfigs.createAggConfig(aggConfig, { addToAggConfigs: false })); - } + aggs.push(agg.aggConfigs.createAggConfig(aggConfig, { addToAggConfigs: false })); + } - return aggs as IBucketAggConfig[]; - }, -}); + return aggs as IBucketAggConfig[]; + }, + }, + { getInternalStartServices } + ); diff --git a/src/plugins/data/public/search/aggs/buckets/histogram.test.ts b/src/plugins/data/public/search/aggs/buckets/histogram.test.ts index c61b4ff37935a5..bbfc263df4268b 100644 --- a/src/plugins/data/public/search/aggs/buckets/histogram.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/histogram.test.ts @@ -17,7 +17,7 @@ * under the License. */ -import { coreMock } from '../../../../../../../src/core/public/mocks'; +import { coreMock, notificationServiceMock } from '../../../../../../../src/core/public/mocks'; import { AggConfigs } from '../agg_configs'; import { mockAggTypesRegistry } from '../test_helpers'; import { BUCKET_TYPES } from './bucket_agg_types'; @@ -27,17 +27,21 @@ import { AutoBounds, HistogramBucketAggDependencies, } from './histogram'; -import { BucketAggType } from './_bucket_agg_type'; +import { BucketAggType } from './bucket_agg_type'; +import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; describe('Histogram Agg', () => { let aggTypesDependencies: HistogramBucketAggDependencies; beforeEach(() => { - const { uiSettings, notifications } = coreMock.createSetup(); + const { uiSettings } = coreMock.createSetup(); aggTypesDependencies = { uiSettings, - notifications, + getInternalStartServices: () => ({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + }), }; }); @@ -87,18 +91,18 @@ describe('Histogram Agg', () => { histogramType = getHistogramBucketAgg(aggTypesDependencies); }); - it('is ordered', () => { + test('is ordered', () => { expect(histogramType.ordered).toBeDefined(); }); - it('is not ordered by date', () => { + test('is not ordered by date', () => { expect(histogramType.ordered).not.toHaveProperty('date'); }); }); describe('params', () => { describe('intervalBase', () => { - it('should not be written to the DSL', () => { + test('should not be written to the DSL', () => { const aggConfigs = getAggConfigs({ intervalBase: 100, field: { @@ -112,7 +116,7 @@ describe('Histogram Agg', () => { }); describe('interval', () => { - it('accepts a whole number', () => { + test('accepts a whole number', () => { const params = getParams({ interval: 100, }); @@ -120,7 +124,7 @@ describe('Histogram Agg', () => { expect(params).toHaveProperty('interval', 100); }); - it('accepts a decimal number', function() { + test('accepts a decimal number', () => { const params = getParams({ interval: 0.1, }); @@ -128,7 +132,7 @@ describe('Histogram Agg', () => { expect(params).toHaveProperty('interval', 0.1); }); - it('accepts a decimal number string', function() { + test('accepts a decimal number string', () => { const params = getParams({ interval: '0.1', }); @@ -136,7 +140,7 @@ describe('Histogram Agg', () => { expect(params).toHaveProperty('interval', 0.1); }); - it('accepts a whole number string', function() { + test('accepts a whole number string', () => { const params = getParams({ interval: '10', }); @@ -144,7 +148,7 @@ describe('Histogram Agg', () => { expect(params).toHaveProperty('interval', 10); }); - it('fails on non-numeric values', function() { + test('fails on non-numeric values', () => { const params = getParams({ interval: [], }); @@ -181,7 +185,7 @@ describe('Histogram Agg', () => { return aggConfig.write(aggConfigs).params; }; - it('will respect the histogram:maxBars setting', () => { + test('will respect the histogram:maxBars setting', () => { const params = getInterval( 5, { interval: 5 }, @@ -194,19 +198,19 @@ describe('Histogram Agg', () => { expect(params).toHaveProperty('interval', 2000); }); - it('will return specified interval, if bars are below histogram:maxBars config', () => { + test('will return specified interval, if bars are below histogram:maxBars config', () => { const params = getInterval(100, { interval: 5 }); expect(params).toHaveProperty('interval', 5); }); - it('will set to intervalBase if interval is below base', () => { + test('will set to intervalBase if interval is below base', () => { const params = getInterval(1000, { interval: 3, intervalBase: 8 }); expect(params).toHaveProperty('interval', 8); }); - it('will round to nearest intervalBase multiple if interval is above base', () => { + test('will round to nearest intervalBase multiple if interval is above base', () => { const roundUp = getInterval(1000, { interval: 46, intervalBase: 10 }); expect(roundUp).toHaveProperty('interval', 50); @@ -214,13 +218,13 @@ describe('Histogram Agg', () => { expect(roundDown).toHaveProperty('interval', 40); }); - it('will not change interval if it is a multiple of base', () => { + test('will not change interval if it is a multiple of base', () => { const output = getInterval(1000, { interval: 35, intervalBase: 5 }); expect(output).toHaveProperty('interval', 35); }); - it('will round to intervalBase after scaling histogram:maxBars', () => { + test('will round to intervalBase after scaling histogram:maxBars', () => { const output = getInterval(100, { interval: 5, intervalBase: 6 }, { min: 0, max: 1000 }); // 100 buckets in 0 to 1000 would result in an interval of 10, so we should @@ -232,7 +236,7 @@ describe('Histogram Agg', () => { describe('min_doc_count', () => { let output: Record; - it('casts true values to 0', () => { + test('casts true values to 0', () => { output = getParams({ min_doc_count: true }); expect(output).toHaveProperty('min_doc_count', 0); @@ -246,7 +250,7 @@ describe('Histogram Agg', () => { expect(output).toHaveProperty('min_doc_count', 0); }); - it('writes 1 for falsy values', () => { + test('writes 1 for falsy values', () => { output = getParams({ min_doc_count: '' }); expect(output).toHaveProperty('min_doc_count', 1); @@ -258,8 +262,8 @@ describe('Histogram Agg', () => { }); }); - describe('extended_bounds', function() { - it('does not write when only eb.min is set', function() { + describe('extended_bounds', () => { + test('does not write when only eb.min is set', () => { const output = getParams({ has_extended_bounds: true, extended_bounds: { min: 0 }, @@ -267,7 +271,7 @@ describe('Histogram Agg', () => { expect(output).not.toHaveProperty('extended_bounds'); }); - it('does not write when only eb.max is set', function() { + test('does not write when only eb.max is set', () => { const output = getParams({ has_extended_bounds: true, extended_bounds: { max: 0 }, @@ -276,7 +280,7 @@ describe('Histogram Agg', () => { expect(output).not.toHaveProperty('extended_bounds'); }); - it('writes when both eb.min and eb.max are set', function() { + test('writes when both eb.min and eb.max are set', () => { const output = getParams({ has_extended_bounds: true, extended_bounds: { min: 99, max: 100 }, @@ -286,7 +290,7 @@ describe('Histogram Agg', () => { expect(output.extended_bounds).toHaveProperty('max', 100); }); - it('does not write when nothing is set', function() { + test('does not write when nothing is set', () => { const output = getParams({ has_extended_bounds: true, extended_bounds: {}, @@ -295,7 +299,7 @@ describe('Histogram Agg', () => { expect(output).not.toHaveProperty('extended_bounds'); }); - it('does not write when has_extended_bounds is false', function() { + test('does not write when has_extended_bounds is false', () => { const output = getParams({ has_extended_bounds: false, extended_bounds: { min: 99, max: 100 }, diff --git a/src/plugins/data/public/search/aggs/buckets/histogram.ts b/src/plugins/data/public/search/aggs/buckets/histogram.ts index bbffc0912bf0d9..f8e8720d24ea97 100644 --- a/src/plugins/data/public/search/aggs/buckets/histogram.ts +++ b/src/plugins/data/public/search/aggs/buckets/histogram.ts @@ -19,12 +19,13 @@ import { get } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { IUiSettingsClient, NotificationsSetup } from 'src/core/public'; +import { IUiSettingsClient } from 'src/core/public'; -import { BucketAggType, IBucketAggConfig } from './_bucket_agg_type'; +import { BucketAggType, IBucketAggConfig } from './bucket_agg_type'; import { createFilterHistogram } from './create_filter/histogram'; import { BUCKET_TYPES } from './bucket_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; +import { GetInternalStartServicesFn } from '../../../types'; export interface AutoBounds { min: number; @@ -33,7 +34,7 @@ export interface AutoBounds { export interface HistogramBucketAggDependencies { uiSettings: IUiSettingsClient; - notifications: NotificationsSetup; + getInternalStartServices: GetInternalStartServicesFn; } export interface IBucketHistogramAggConfig extends IBucketAggConfig { @@ -43,164 +44,167 @@ export interface IBucketHistogramAggConfig extends IBucketAggConfig { export const getHistogramBucketAgg = ({ uiSettings, - notifications, + getInternalStartServices, }: HistogramBucketAggDependencies) => - new BucketAggType({ - name: BUCKET_TYPES.HISTOGRAM, - title: i18n.translate('data.search.aggs.buckets.histogramTitle', { - defaultMessage: 'Histogram', - }), - ordered: {}, - makeLabel(aggConfig) { - return aggConfig.getFieldDisplayName(); - }, - createFilter: createFilterHistogram, - decorateAggConfig() { - let autoBounds: AutoBounds; - - return { - setAutoBounds: { - configurable: true, - value(newValue: AutoBounds) { - autoBounds = newValue; + new BucketAggType( + { + name: BUCKET_TYPES.HISTOGRAM, + title: i18n.translate('data.search.aggs.buckets.histogramTitle', { + defaultMessage: 'Histogram', + }), + ordered: {}, + makeLabel(aggConfig) { + return aggConfig.getFieldDisplayName(); + }, + createFilter: createFilterHistogram, + decorateAggConfig() { + let autoBounds: AutoBounds; + + return { + setAutoBounds: { + configurable: true, + value(newValue: AutoBounds) { + autoBounds = newValue; + }, }, - }, - getAutoBounds: { - configurable: true, - value() { - return autoBounds; + getAutoBounds: { + configurable: true, + value() { + return autoBounds; + }, }, - }, - }; - }, - params: [ - { - name: 'field', - type: 'field', - filterFieldTypes: KBN_FIELD_TYPES.NUMBER, - }, - { - /* - * This parameter can be set if you want the auto scaled interval to always - * be a multiple of a specific base. - */ - name: 'intervalBase', - default: null, - write: () => {}, + }; }, - { - name: 'interval', - modifyAggConfigOnSearchRequestStart( - aggConfig: IBucketHistogramAggConfig, - searchSource: any, - options: any - ) { - const field = aggConfig.getField(); - const aggBody = field.scripted - ? { script: { source: field.script, lang: field.lang } } - : { field: field.name }; - - const childSearchSource = searchSource - .createChild() - .setField('size', 0) - .setField('aggs', { - maxAgg: { - max: aggBody, - }, - minAgg: { - min: aggBody, - }, - }); - - return childSearchSource - .fetch(options) - .then((resp: any) => { - aggConfig.setAutoBounds({ - min: get(resp, 'aggregations.minAgg.value'), - max: get(resp, 'aggregations.maxAgg.value'), - }); - }) - .catch((e: Error) => { - if (e.name === 'AbortError') return; - notifications.toasts.addWarning( - i18n.translate('data.search.aggs.histogram.missingMaxMinValuesWarning', { - defaultMessage: - 'Unable to retrieve max and min values to auto-scale histogram buckets. This may lead to poor visualization performance.', - }) - ); - }); + params: [ + { + name: 'field', + type: 'field', + filterFieldTypes: KBN_FIELD_TYPES.NUMBER, + }, + { + /* + * This parameter can be set if you want the auto scaled interval to always + * be a multiple of a specific base. + */ + name: 'intervalBase', + default: null, + write: () => {}, }, - write(aggConfig, output) { - let interval = parseFloat(aggConfig.params.interval); - if (interval <= 0) { - interval = 1; - } - const autoBounds = aggConfig.getAutoBounds(); - - // ensure interval does not create too many buckets and crash browser - if (autoBounds) { - const range = autoBounds.max - autoBounds.min; - const bars = range / interval; - - if (bars > uiSettings.get('histogram:maxBars')) { - const minInterval = range / uiSettings.get('histogram:maxBars'); - - // Round interval by order of magnitude to provide clean intervals - // Always round interval up so there will always be less buckets than histogram:maxBars - const orderOfMagnitude = Math.pow(10, Math.floor(Math.log10(minInterval))); - let roundInterval = orderOfMagnitude; - - while (roundInterval < minInterval) { - roundInterval += orderOfMagnitude; + { + name: 'interval', + modifyAggConfigOnSearchRequestStart( + aggConfig: IBucketHistogramAggConfig, + searchSource: any, + options: any + ) { + const field = aggConfig.getField(); + const aggBody = field.scripted + ? { script: { source: field.script, lang: field.lang } } + : { field: field.name }; + + const childSearchSource = searchSource + .createChild() + .setField('size', 0) + .setField('aggs', { + maxAgg: { + max: aggBody, + }, + minAgg: { + min: aggBody, + }, + }); + + return childSearchSource + .fetch(options) + .then((resp: any) => { + aggConfig.setAutoBounds({ + min: get(resp, 'aggregations.minAgg.value'), + max: get(resp, 'aggregations.maxAgg.value'), + }); + }) + .catch((e: Error) => { + if (e.name === 'AbortError') return; + getInternalStartServices().notifications.toasts.addWarning( + i18n.translate('data.search.aggs.histogram.missingMaxMinValuesWarning', { + defaultMessage: + 'Unable to retrieve max and min values to auto-scale histogram buckets. This may lead to poor visualization performance.', + }) + ); + }); + }, + write(aggConfig, output) { + let interval = parseFloat(aggConfig.params.interval); + if (interval <= 0) { + interval = 1; + } + const autoBounds = aggConfig.getAutoBounds(); + + // ensure interval does not create too many buckets and crash browser + if (autoBounds) { + const range = autoBounds.max - autoBounds.min; + const bars = range / interval; + + if (bars > uiSettings.get('histogram:maxBars')) { + const minInterval = range / uiSettings.get('histogram:maxBars'); + + // Round interval by order of magnitude to provide clean intervals + // Always round interval up so there will always be less buckets than histogram:maxBars + const orderOfMagnitude = Math.pow(10, Math.floor(Math.log10(minInterval))); + let roundInterval = orderOfMagnitude; + + while (roundInterval < minInterval) { + roundInterval += orderOfMagnitude; + } + interval = roundInterval; } - interval = roundInterval; } - } - const base = aggConfig.params.intervalBase; - - if (base) { - if (interval < base) { - // In case the specified interval is below the base, just increase it to it's base - interval = base; - } else if (interval % base !== 0) { - // In case the interval is not a multiple of the base round it to the next base - interval = Math.round(interval / base) * base; + const base = aggConfig.params.intervalBase; + + if (base) { + if (interval < base) { + // In case the specified interval is below the base, just increase it to it's base + interval = base; + } else if (interval % base !== 0) { + // In case the interval is not a multiple of the base round it to the next base + interval = Math.round(interval / base) * base; + } } - } - output.params.interval = interval; + output.params.interval = interval; + }, }, - }, - { - name: 'min_doc_count', - default: false, - write(aggConfig, output) { - if (aggConfig.params.min_doc_count) { - output.params.min_doc_count = 0; - } else { - output.params.min_doc_count = 1; - } + { + name: 'min_doc_count', + default: false, + write(aggConfig, output) { + if (aggConfig.params.min_doc_count) { + output.params.min_doc_count = 0; + } else { + output.params.min_doc_count = 1; + } + }, }, - }, - { - name: 'has_extended_bounds', - default: false, - write: () => {}, - }, - { - name: 'extended_bounds', - default: { - min: '', - max: '', + { + name: 'has_extended_bounds', + default: false, + write: () => {}, }, - write(aggConfig, output) { - const { min, max } = aggConfig.params.extended_bounds; + { + name: 'extended_bounds', + default: { + min: '', + max: '', + }, + write(aggConfig, output) { + const { min, max } = aggConfig.params.extended_bounds; - if (aggConfig.params.has_extended_bounds && (min || min === 0) && (max || max === 0)) { - output.params.extended_bounds = { min, max }; - } + if (aggConfig.params.has_extended_bounds && (min || min === 0) && (max || max === 0)) { + output.params.extended_bounds = { min, max }; + } + }, + shouldShow: (aggConfig: IBucketAggConfig) => aggConfig.params.has_extended_bounds, }, - shouldShow: (aggConfig: IBucketAggConfig) => aggConfig.params.has_extended_bounds, - }, - ], - }); + ], + }, + { getInternalStartServices } + ); diff --git a/src/plugins/data/public/search/aggs/buckets/ip_range.ts b/src/plugins/data/public/search/aggs/buckets/ip_range.ts index da6866d40a52fe..bde347d6e673d1 100644 --- a/src/plugins/data/public/search/aggs/buckets/ip_range.ts +++ b/src/plugins/data/public/search/aggs/buckets/ip_range.ts @@ -19,77 +19,85 @@ import { noop, map, omit, isNull } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { BucketAggType } from './_bucket_agg_type'; +import { BucketAggType } from './bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; import { createFilterIpRange } from './create_filter/ip_range'; import { IpRangeKey, convertIPRangeToString } from './lib/ip_range'; import { KBN_FIELD_TYPES, FieldFormat, TEXT_CONTEXT_TYPE } from '../../../../common'; -import { getFieldFormats } from '../../../../public/services'; +import { GetInternalStartServicesFn } from '../../../types'; const ipRangeTitle = i18n.translate('data.search.aggs.buckets.ipRangeTitle', { defaultMessage: 'IPv4 Range', }); -export const ipRangeBucketAgg = new BucketAggType({ - name: BUCKET_TYPES.IP_RANGE, - title: ipRangeTitle, - createFilter: createFilterIpRange, - getKey(bucket, key, agg): IpRangeKey { - if (agg.params.ipRangeType === 'mask') { - return { type: 'mask', mask: key }; - } - return { type: 'range', from: bucket.from, to: bucket.to }; - }, - getFormat(agg) { - const fieldFormatsService = getFieldFormats(); - const formatter = agg.fieldOwnFormatter( - TEXT_CONTEXT_TYPE, - fieldFormatsService.getDefaultInstance(KBN_FIELD_TYPES.IP) - ); - const IpRangeFormat = FieldFormat.from(function(range: IpRangeKey) { - return convertIPRangeToString(range, formatter); - }); - return new IpRangeFormat(); - }, - makeLabel(aggConfig) { - return i18n.translate('data.search.aggs.buckets.ipRangeLabel', { - defaultMessage: '{fieldName} IP ranges', - values: { - fieldName: aggConfig.getFieldDisplayName(), - }, - }); - }, - params: [ - { - name: 'field', - type: 'field', - filterFieldTypes: KBN_FIELD_TYPES.IP, - }, - { - name: 'ipRangeType', - default: 'fromTo', - write: noop, - }, +export interface IpRangeBucketAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} + +export const getIpRangeBucketAgg = ({ getInternalStartServices }: IpRangeBucketAggDependencies) => + new BucketAggType( { - name: 'ranges', - default: { - fromTo: [ - { from: '0.0.0.0', to: '127.255.255.255' }, - { from: '128.0.0.0', to: '191.255.255.255' }, - ], - mask: [{ mask: '0.0.0.0/1' }, { mask: '128.0.0.0/2' }], + name: BUCKET_TYPES.IP_RANGE, + title: ipRangeTitle, + createFilter: createFilterIpRange, + getKey(bucket, key, agg): IpRangeKey { + if (agg.params.ipRangeType === 'mask') { + return { type: 'mask', mask: key }; + } + return { type: 'range', from: bucket.from, to: bucket.to }; + }, + getFormat(agg) { + const { fieldFormats } = getInternalStartServices(); + const formatter = agg.fieldOwnFormatter( + TEXT_CONTEXT_TYPE, + fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.IP) + ); + const IpRangeFormat = FieldFormat.from(function(range: IpRangeKey) { + return convertIPRangeToString(range, formatter); + }); + return new IpRangeFormat(); + }, + makeLabel(aggConfig) { + return i18n.translate('data.search.aggs.buckets.ipRangeLabel', { + defaultMessage: '{fieldName} IP ranges', + values: { + fieldName: aggConfig.getFieldDisplayName(), + }, + }); }, - write(aggConfig, output) { - const ipRangeType = aggConfig.params.ipRangeType; - let ranges = aggConfig.params.ranges[ipRangeType]; + params: [ + { + name: 'field', + type: 'field', + filterFieldTypes: KBN_FIELD_TYPES.IP, + }, + { + name: 'ipRangeType', + default: 'fromTo', + write: noop, + }, + { + name: 'ranges', + default: { + fromTo: [ + { from: '0.0.0.0', to: '127.255.255.255' }, + { from: '128.0.0.0', to: '191.255.255.255' }, + ], + mask: [{ mask: '0.0.0.0/1' }, { mask: '128.0.0.0/2' }], + }, + write(aggConfig, output) { + const ipRangeType = aggConfig.params.ipRangeType; + let ranges = aggConfig.params.ranges[ipRangeType]; - if (ipRangeType === 'fromTo') { - ranges = map(ranges, (range: any) => omit(range, isNull)); - } + if (ipRangeType === 'fromTo') { + ranges = map(ranges, (range: any) => omit(range, isNull)); + } - output.params.ranges = ranges; - }, + output.params.ranges = ranges; + }, + }, + ], }, - ], -}); + { getInternalStartServices } + ); diff --git a/src/plugins/data/public/search/aggs/buckets/migrate_include_exclude_format.ts b/src/plugins/data/public/search/aggs/buckets/migrate_include_exclude_format.ts index d94477b588f8d1..0beeb1c3722752 100644 --- a/src/plugins/data/public/search/aggs/buckets/migrate_include_exclude_format.ts +++ b/src/plugins/data/public/search/aggs/buckets/migrate_include_exclude_format.ts @@ -18,7 +18,7 @@ */ import { isString, isObject } from 'lodash'; -import { IBucketAggConfig, BucketAggType, BucketAggParam } from './_bucket_agg_type'; +import { IBucketAggConfig, BucketAggType, BucketAggParam } from './bucket_agg_type'; import { IAggConfig } from '../agg_config'; export const isType = (type: string) => { diff --git a/src/plugins/data/public/search/aggs/buckets/range.test.ts b/src/plugins/data/public/search/aggs/buckets/range.test.ts index bf3711543ae880..a1f0ab6a2326a2 100644 --- a/src/plugins/data/public/search/aggs/buckets/range.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/range.test.ts @@ -17,11 +17,13 @@ * under the License. */ -import { rangeBucketAgg } from './range'; +import { getRangeBucketAgg, RangeBucketAggDependencies } from './range'; import { AggConfigs } from '../agg_configs'; import { mockDataServices, mockAggTypesRegistry } from '../test_helpers'; import { BUCKET_TYPES } from './bucket_agg_types'; import { FieldFormatsGetConfigFn, NumberFormat } from '../../../../common'; +import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; +import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; const buckets = [ { @@ -44,7 +46,16 @@ const buckets = [ ]; describe('Range Agg', () => { + let aggTypesDependencies: RangeBucketAggDependencies; + beforeEach(() => { + aggTypesDependencies = { + getInternalStartServices: () => ({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + }), + }; + mockDataServices(); }); @@ -84,15 +95,14 @@ describe('Range Agg', () => { }, }, ], - { typesRegistry: mockAggTypesRegistry([rangeBucketAgg]) } + { typesRegistry: mockAggTypesRegistry([getRangeBucketAgg(aggTypesDependencies)]) } ); }; describe('formating', () => { - it('formats bucket keys properly', () => { + test('formats bucket keys properly', () => { const aggConfigs = getAggConfigs(); const agg = aggConfigs.aggs[0]; - const format = (val: any) => agg.fieldFormatter()(agg.getKey(val)); expect(format(buckets[0])).toBe('≥ -∞ and < 1 KB'); diff --git a/src/plugins/data/public/search/aggs/buckets/range.ts b/src/plugins/data/public/search/aggs/buckets/range.ts index 036a0d4c1e8dae..2c1303814a88ab 100644 --- a/src/plugins/data/public/search/aggs/buckets/range.ts +++ b/src/plugins/data/public/search/aggs/buckets/range.ts @@ -18,11 +18,12 @@ */ import { i18n } from '@kbn/i18n'; -import { BucketAggType } from './_bucket_agg_type'; +import { BucketAggType } from './bucket_agg_type'; import { FieldFormat, KBN_FIELD_TYPES } from '../../../../common'; import { RangeKey } from './range_key'; import { createFilterRange } from './create_filter/range'; import { BUCKET_TYPES } from './bucket_agg_types'; +import { GetInternalStartServicesFn } from '../../../types'; const keyCaches = new WeakMap(); const formats = new WeakMap(); @@ -31,76 +32,84 @@ const rangeTitle = i18n.translate('data.search.aggs.buckets.rangeTitle', { defaultMessage: 'Range', }); -export const rangeBucketAgg = new BucketAggType({ - name: BUCKET_TYPES.RANGE, - title: rangeTitle, - createFilter: createFilterRange, - makeLabel(aggConfig) { - return i18n.translate('data.search.aggs.aggTypesLabel', { - defaultMessage: '{fieldName} ranges', - values: { - fieldName: aggConfig.getFieldDisplayName(), +export interface RangeBucketAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} + +export const getRangeBucketAgg = ({ getInternalStartServices }: RangeBucketAggDependencies) => + new BucketAggType( + { + name: BUCKET_TYPES.RANGE, + title: rangeTitle, + createFilter: createFilterRange, + makeLabel(aggConfig) { + return i18n.translate('data.search.aggs.aggTypesLabel', { + defaultMessage: '{fieldName} ranges', + values: { + fieldName: aggConfig.getFieldDisplayName(), + }, + }); }, - }); - }, - getKey(bucket, key, agg) { - let keys = keyCaches.get(agg); + getKey(bucket, key, agg) { + let keys = keyCaches.get(agg); - if (!keys) { - keys = new Map(); - keyCaches.set(agg, keys); - } + if (!keys) { + keys = new Map(); + keyCaches.set(agg, keys); + } - const id = RangeKey.idBucket(bucket); + const id = RangeKey.idBucket(bucket); - key = keys.get(id); - if (!key) { - key = new RangeKey(bucket); - keys.set(id, key); - } + key = keys.get(id); + if (!key) { + key = new RangeKey(bucket); + keys.set(id, key); + } - return key; - }, - getFormat(agg) { - let aggFormat = formats.get(agg); - if (aggFormat) return aggFormat; + return key; + }, + getFormat(agg) { + let aggFormat = formats.get(agg); + if (aggFormat) return aggFormat; - const RangeFormat = FieldFormat.from((range: any) => { - const format = agg.fieldOwnFormatter(); - const gte = '\u2265'; - const lt = '\u003c'; - return i18n.translate('data.search.aggs.aggTypes.rangesFormatMessage', { - defaultMessage: '{gte} {from} and {lt} {to}', - values: { - gte, - from: format(range.gte), - lt, - to: format(range.lt), - }, - }); - }); + const RangeFormat = FieldFormat.from((range: any) => { + const format = agg.fieldOwnFormatter(); + const gte = '\u2265'; + const lt = '\u003c'; + return i18n.translate('data.search.aggs.aggTypes.rangesFormatMessage', { + defaultMessage: '{gte} {from} and {lt} {to}', + values: { + gte, + from: format(range.gte), + lt, + to: format(range.lt), + }, + }); + }); - aggFormat = new RangeFormat(); + aggFormat = new RangeFormat(); - formats.set(agg, aggFormat); - return aggFormat; - }, - params: [ - { - name: 'field', - type: 'field', - filterFieldTypes: [KBN_FIELD_TYPES.NUMBER], - }, - { - name: 'ranges', - default: [ - { from: 0, to: 1000 }, - { from: 1000, to: 2000 }, - ], - write(aggConfig, output) { - output.params.ranges = aggConfig.params.ranges; - output.params.keyed = true; + formats.set(agg, aggFormat); + return aggFormat; }, + params: [ + { + name: 'field', + type: 'field', + filterFieldTypes: [KBN_FIELD_TYPES.NUMBER], + }, + { + name: 'ranges', + default: [ + { from: 0, to: 1000 }, + { from: 1000, to: 2000 }, + ], + write(aggConfig, output) { + output.params.ranges = aggConfig.params.ranges; + output.params.keyed = true; + }, + }, + ], }, - ], -}); + { getInternalStartServices } + ); diff --git a/src/plugins/data/public/search/aggs/buckets/significant_terms.test.ts b/src/plugins/data/public/search/aggs/buckets/significant_terms.test.ts index 1c221126c35e03..761d0ced6a1146 100644 --- a/src/plugins/data/public/search/aggs/buckets/significant_terms.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/significant_terms.test.ts @@ -20,12 +20,27 @@ import { AggConfigs, IAggConfigs } from '../agg_configs'; import { mockAggTypesRegistry } from '../test_helpers'; import { BUCKET_TYPES } from './bucket_agg_types'; -import { significantTermsBucketAgg } from './significant_terms'; -import { IBucketAggConfig } from './_bucket_agg_type'; +import { + getSignificantTermsBucketAgg, + SignificantTermsBucketAggDependencies, +} from './significant_terms'; +import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; +import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; describe('Significant Terms Agg', () => { describe('order agg editor UI', () => { describe('convert include/exclude from old format', () => { + let aggTypesDependencies: SignificantTermsBucketAggDependencies; + + beforeEach(() => { + aggTypesDependencies = { + getInternalStartServices: () => ({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + }), + }; + }); + const getAggConfigs = (params: Record = {}) => { const indexPattern = { id: '1234', @@ -51,7 +66,11 @@ describe('Significant Terms Agg', () => { params, }, ], - { typesRegistry: mockAggTypesRegistry([significantTermsBucketAgg]) } + { + typesRegistry: mockAggTypesRegistry([ + getSignificantTermsBucketAgg(aggTypesDependencies), + ]), + } ); }; @@ -64,19 +83,19 @@ describe('Significant Terms Agg', () => { expect(params.exclude).toBe('400'); }; - it('should generate correct label', () => { + test('should generate correct label', () => { const aggConfigs = getAggConfigs({ size: 'SIZE', field: { name: 'FIELD', }, }); - const label = significantTermsBucketAgg.makeLabel(aggConfigs.aggs[0] as IBucketAggConfig); + const label = aggConfigs.aggs[0].makeLabel(); expect(label).toBe('Top SIZE unusual terms in FIELD'); }); - it('should doesnt do anything with string type', () => { + test('should doesnt do anything with string type', () => { const aggConfigs = getAggConfigs({ include: '404', exclude: '400', @@ -89,7 +108,7 @@ describe('Significant Terms Agg', () => { testSerializeAndWrite(aggConfigs); }); - it('should converts object to string type', () => { + test('should converts object to string type', () => { const aggConfigs = getAggConfigs({ include: { pattern: '404', diff --git a/src/plugins/data/public/search/aggs/buckets/significant_terms.ts b/src/plugins/data/public/search/aggs/buckets/significant_terms.ts index f12ebe58e2de28..49d797f3afbc9a 100644 --- a/src/plugins/data/public/search/aggs/buckets/significant_terms.ts +++ b/src/plugins/data/public/search/aggs/buckets/significant_terms.ts @@ -18,59 +18,72 @@ */ import { i18n } from '@kbn/i18n'; -import { BucketAggType } from './_bucket_agg_type'; +import { BucketAggType } from './bucket_agg_type'; import { createFilterTerms } from './create_filter/terms'; import { isStringType, migrateIncludeExcludeFormat } from './migrate_include_exclude_format'; import { BUCKET_TYPES } from './bucket_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; +import { GetInternalStartServicesFn } from '../../../types'; const significantTermsTitle = i18n.translate('data.search.aggs.buckets.significantTermsTitle', { defaultMessage: 'Significant Terms', }); -export const significantTermsBucketAgg = new BucketAggType({ - name: BUCKET_TYPES.SIGNIFICANT_TERMS, - title: significantTermsTitle, - makeLabel(aggConfig) { - return i18n.translate('data.search.aggs.buckets.significantTermsLabel', { - defaultMessage: 'Top {size} unusual terms in {fieldName}', - values: { - size: aggConfig.params.size, - fieldName: aggConfig.getFieldDisplayName(), - }, - }); - }, - createFilter: createFilterTerms, - params: [ - { - name: 'field', - type: 'field', - scriptable: false, - filterFieldTypes: KBN_FIELD_TYPES.STRING, - }, - { - name: 'size', - default: '', - }, +export interface SignificantTermsBucketAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} + +export const getSignificantTermsBucketAgg = ({ + getInternalStartServices, +}: SignificantTermsBucketAggDependencies) => + new BucketAggType( { - name: 'exclude', - displayName: i18n.translate('data.search.aggs.buckets.significantTerms.excludeLabel', { - defaultMessage: 'Exclude', - }), - type: 'string', - advanced: true, - shouldShow: isStringType, - ...migrateIncludeExcludeFormat, + name: BUCKET_TYPES.SIGNIFICANT_TERMS, + title: significantTermsTitle, + makeLabel(aggConfig) { + return i18n.translate('data.search.aggs.buckets.significantTermsLabel', { + defaultMessage: 'Top {size} unusual terms in {fieldName}', + values: { + size: aggConfig.params.size, + fieldName: aggConfig.getFieldDisplayName(), + }, + }); + }, + createFilter: createFilterTerms, + params: [ + { + name: 'field', + type: 'field', + scriptable: false, + filterFieldTypes: KBN_FIELD_TYPES.STRING, + }, + { + name: 'size', + default: '', + }, + { + name: 'exclude', + displayName: i18n.translate('data.search.aggs.buckets.significantTerms.excludeLabel', { + defaultMessage: 'Exclude', + }), + type: 'string', + advanced: true, + shouldShow: isStringType, + ...migrateIncludeExcludeFormat, + }, + { + name: 'include', + displayName: i18n.translate('data.search.aggs.buckets.significantTerms.includeLabel', { + defaultMessage: 'Include', + }), + type: 'string', + advanced: true, + shouldShow: isStringType, + ...migrateIncludeExcludeFormat, + }, + ], }, { - name: 'include', - displayName: i18n.translate('data.search.aggs.buckets.significantTerms.includeLabel', { - defaultMessage: 'Include', - }), - type: 'string', - advanced: true, - shouldShow: isStringType, - ...migrateIncludeExcludeFormat, - }, - ], -}); + getInternalStartServices, + } + ); diff --git a/src/plugins/data/public/search/aggs/buckets/terms.test.ts b/src/plugins/data/public/search/aggs/buckets/terms.test.ts index 280d78f6620bd0..5afe7d0b0c35c0 100644 --- a/src/plugins/data/public/search/aggs/buckets/terms.test.ts +++ b/src/plugins/data/public/search/aggs/buckets/terms.test.ts @@ -51,7 +51,7 @@ describe('Terms Agg', () => { ); }; - it('converts object to string type', function() { + test('converts object to string type', () => { const aggConfigs = getAggConfigs({ include: { pattern: '404', diff --git a/src/plugins/data/public/search/aggs/buckets/terms.ts b/src/plugins/data/public/search/aggs/buckets/terms.ts index 813c657934a769..5baa38af0e8d61 100644 --- a/src/plugins/data/public/search/aggs/buckets/terms.ts +++ b/src/plugins/data/public/search/aggs/buckets/terms.ts @@ -19,9 +19,8 @@ import { noop } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { BucketAggType } from './_bucket_agg_type'; +import { BucketAggType, IBucketAggConfig } from './bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; -import { IBucketAggConfig } from './_bucket_agg_type'; import { createFilterTerms } from './create_filter/terms'; import { isStringType, migrateIncludeExcludeFormat } from './migrate_include_exclude_format'; import { IAggConfigs } from '../agg_configs'; @@ -36,6 +35,7 @@ import { mergeOtherBucketAggResponse, updateMissingBucket, } from './_terms_other_bucket_helper'; +import { GetInternalStartServicesFn } from '../../../types'; export const termsAggFilter = [ '!top_hits', @@ -56,220 +56,230 @@ const termsTitle = i18n.translate('data.search.aggs.buckets.termsTitle', { defaultMessage: 'Terms', }); -export const termsBucketAgg = new BucketAggType({ - name: BUCKET_TYPES.TERMS, - title: termsTitle, - makeLabel(agg) { - const params = agg.params; - return agg.getFieldDisplayName() + ': ' + params.order.text; - }, - getFormat(bucket): IFieldFormat { - return { - getConverterFor: (type: FieldFormatsContentType) => { - return (val: any) => { - if (val === '__other__') { - return bucket.params.otherBucketLabel; - } - if (val === '__missing__') { - return bucket.params.missingBucketLabel; - } +export interface TermsBucketAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} - return bucket.params.field.format.convert(val, type); - }; +export const getTermsBucketAgg = ({ getInternalStartServices }: TermsBucketAggDependencies) => + new BucketAggType( + { + name: BUCKET_TYPES.TERMS, + title: termsTitle, + makeLabel(agg) { + const params = agg.params; + return agg.getFieldDisplayName() + ': ' + params.order.text; }, - } as IFieldFormat; - }, - createFilter: createFilterTerms, - postFlightRequest: async ( - resp: any, - aggConfigs: IAggConfigs, - aggConfig: IBucketAggConfig, - searchSource: ISearchSource, - inspectorAdapters: Adapters, - abortSignal?: AbortSignal - ) => { - if (!resp.aggregations) return resp; - const nestedSearchSource = searchSource.createChild(); - if (aggConfig.params.otherBucket) { - const filterAgg = buildOtherBucketAgg(aggConfigs, aggConfig, resp); - if (!filterAgg) return resp; + getFormat(bucket): IFieldFormat { + return { + getConverterFor: (type: FieldFormatsContentType) => { + return (val: any) => { + if (val === '__other__') { + return bucket.params.otherBucketLabel; + } + if (val === '__missing__') { + return bucket.params.missingBucketLabel; + } - nestedSearchSource.setField('aggs', filterAgg); + return bucket.params.field.format.convert(val, type); + }; + }, + } as IFieldFormat; + }, + createFilter: createFilterTerms, + postFlightRequest: async ( + resp: any, + aggConfigs: IAggConfigs, + aggConfig: IBucketAggConfig, + searchSource: ISearchSource, + inspectorAdapters: Adapters, + abortSignal?: AbortSignal + ) => { + if (!resp.aggregations) return resp; + const nestedSearchSource = searchSource.createChild(); + if (aggConfig.params.otherBucket) { + const filterAgg = buildOtherBucketAgg(aggConfigs, aggConfig, resp); + if (!filterAgg) return resp; - const request = inspectorAdapters.requests.start( - i18n.translate('data.search.aggs.buckets.terms.otherBucketTitle', { - defaultMessage: 'Other bucket', - }), - { - description: i18n.translate('data.search.aggs.buckets.terms.otherBucketDescription', { - defaultMessage: - 'This request counts the number of documents that fall ' + - 'outside the criterion of the data buckets.', - }), - } - ); - nestedSearchSource.getSearchRequestBody().then((body: string) => { - request.json(body); - }); - request.stats(getRequestInspectorStats(nestedSearchSource)); + nestedSearchSource.setField('aggs', filterAgg); - const response = await nestedSearchSource.fetch({ abortSignal }); - request.stats(getResponseInspectorStats(nestedSearchSource, response)).ok({ json: response }); - resp = mergeOtherBucketAggResponse(aggConfigs, resp, response, aggConfig, filterAgg()); - } - if (aggConfig.params.missingBucket) { - resp = updateMissingBucket(resp, aggConfigs, aggConfig); - } - return resp; - }, - params: [ - { - name: 'field', - type: 'field', - filterFieldTypes: [ - KBN_FIELD_TYPES.NUMBER, - KBN_FIELD_TYPES.BOOLEAN, - KBN_FIELD_TYPES.DATE, - KBN_FIELD_TYPES.IP, - KBN_FIELD_TYPES.STRING, - ], - }, - { - name: 'orderBy', - write: noop, // prevent default write, it's handled by orderAgg - }, - { - name: 'orderAgg', - type: 'agg', - allowedAggs: termsAggFilter, - default: null, - makeAgg(termsAgg, state) { - state = state || {}; - state.schema = 'orderAgg'; - const orderAgg = termsAgg.aggConfigs.createAggConfig(state, { - addToAggConfigs: false, - }); - orderAgg.id = termsAgg.id + '-orderAgg'; + const request = inspectorAdapters.requests.start( + i18n.translate('data.search.aggs.buckets.terms.otherBucketTitle', { + defaultMessage: 'Other bucket', + }), + { + description: i18n.translate('data.search.aggs.buckets.terms.otherBucketDescription', { + defaultMessage: + 'This request counts the number of documents that fall ' + + 'outside the criterion of the data buckets.', + }), + } + ); + nestedSearchSource.getSearchRequestBody().then((body: string) => { + request.json(body); + }); + request.stats(getRequestInspectorStats(nestedSearchSource)); - return orderAgg; + const response = await nestedSearchSource.fetch({ abortSignal }); + request + .stats(getResponseInspectorStats(nestedSearchSource, response)) + .ok({ json: response }); + resp = mergeOtherBucketAggResponse(aggConfigs, resp, response, aggConfig, filterAgg()); + } + if (aggConfig.params.missingBucket) { + resp = updateMissingBucket(resp, aggConfigs, aggConfig); + } + return resp; }, - write(agg, output, aggs) { - const dir = agg.params.order.value; - const order: Record = (output.params.order = {}); + params: [ + { + name: 'field', + type: 'field', + filterFieldTypes: [ + KBN_FIELD_TYPES.NUMBER, + KBN_FIELD_TYPES.BOOLEAN, + KBN_FIELD_TYPES.DATE, + KBN_FIELD_TYPES.IP, + KBN_FIELD_TYPES.STRING, + ], + }, + { + name: 'orderBy', + write: noop, // prevent default write, it's handled by orderAgg + }, + { + name: 'orderAgg', + type: 'agg', + allowedAggs: termsAggFilter, + default: null, + makeAgg(termsAgg, state) { + state = state || {}; + state.schema = 'orderAgg'; + const orderAgg = termsAgg.aggConfigs.createAggConfig(state, { + addToAggConfigs: false, + }); + orderAgg.id = termsAgg.id + '-orderAgg'; - let orderAgg = agg.params.orderAgg || aggs!.getResponseAggById(agg.params.orderBy); + return orderAgg; + }, + write(agg, output, aggs) { + const dir = agg.params.order.value; + const order: Record = (output.params.order = {}); - // TODO: This works around an Elasticsearch bug the always casts terms agg scripts to strings - // thus causing issues with filtering. This probably causes other issues since float might not - // be able to contain the number on the elasticsearch side - if (output.params.script) { - output.params.value_type = - agg.getField().type === 'number' ? 'float' : agg.getField().type; - } + let orderAgg = agg.params.orderAgg || aggs!.getResponseAggById(agg.params.orderBy); - if (agg.params.missingBucket && agg.params.field.type === 'string') { - output.params.missing = '__missing__'; - } + // TODO: This works around an Elasticsearch bug the always casts terms agg scripts to strings + // thus causing issues with filtering. This probably causes other issues since float might not + // be able to contain the number on the elasticsearch side + if (output.params.script) { + output.params.value_type = + agg.getField().type === 'number' ? 'float' : agg.getField().type; + } - if (!orderAgg) { - order[agg.params.orderBy || '_count'] = dir; - return; - } + if (agg.params.missingBucket && agg.params.field.type === 'string') { + output.params.missing = '__missing__'; + } - if (orderAgg.type.name === 'count') { - order._count = dir; - return; - } + if (!orderAgg) { + order[agg.params.orderBy || '_count'] = dir; + return; + } - const orderAggId = orderAgg.id; + if (orderAgg.type.name === 'count') { + order._count = dir; + return; + } - if (orderAgg.parentId && aggs) { - orderAgg = aggs.byId(orderAgg.parentId); - } + const orderAggId = orderAgg.id; - output.subAggs = (output.subAggs || []).concat(orderAgg); - order[orderAggId] = dir; - }, - }, - { - name: 'order', - type: 'optioned', - default: 'desc', - options: [ + if (orderAgg.parentId && aggs) { + orderAgg = aggs.byId(orderAgg.parentId); + } + + output.subAggs = (output.subAggs || []).concat(orderAgg); + order[orderAggId] = dir; + }, + }, { - text: i18n.translate('data.search.aggs.buckets.terms.orderDescendingTitle', { - defaultMessage: 'Descending', + name: 'order', + type: 'optioned', + default: 'desc', + options: [ + { + text: i18n.translate('data.search.aggs.buckets.terms.orderDescendingTitle', { + defaultMessage: 'Descending', + }), + value: 'desc', + }, + { + text: i18n.translate('data.search.aggs.buckets.terms.orderAscendingTitle', { + defaultMessage: 'Ascending', + }), + value: 'asc', + }, + ], + write: noop, // prevent default write, it's handled by orderAgg + }, + { + name: 'size', + default: 5, + }, + { + name: 'otherBucket', + default: false, + write: noop, + }, + { + name: 'otherBucketLabel', + type: 'string', + default: i18n.translate('data.search.aggs.buckets.terms.otherBucketLabel', { + defaultMessage: 'Other', + }), + displayName: i18n.translate('data.search.aggs.otherBucket.labelForOtherBucketLabel', { + defaultMessage: 'Label for other bucket', }), - value: 'desc', + shouldShow: agg => agg.getParam('otherBucket'), + write: noop, }, { - text: i18n.translate('data.search.aggs.buckets.terms.orderAscendingTitle', { - defaultMessage: 'Ascending', + name: 'missingBucket', + default: false, + write: noop, + }, + { + name: 'missingBucketLabel', + default: i18n.translate('data.search.aggs.buckets.terms.missingBucketLabel', { + defaultMessage: 'Missing', + description: `Default label used in charts when documents are missing a field. + Visible when you create a chart with a terms aggregation and enable "Show missing values"`, + }), + type: 'string', + displayName: i18n.translate('data.search.aggs.otherBucket.labelForMissingValuesLabel', { + defaultMessage: 'Label for missing values', }), - value: 'asc', + shouldShow: agg => agg.getParam('missingBucket'), + write: noop, + }, + { + name: 'exclude', + displayName: i18n.translate('data.search.aggs.buckets.terms.excludeLabel', { + defaultMessage: 'Exclude', + }), + type: 'string', + advanced: true, + shouldShow: isStringType, + ...migrateIncludeExcludeFormat, + }, + { + name: 'include', + displayName: i18n.translate('data.search.aggs.buckets.terms.includeLabel', { + defaultMessage: 'Include', + }), + type: 'string', + advanced: true, + shouldShow: isStringType, + ...migrateIncludeExcludeFormat, }, ], - write: noop, // prevent default write, it's handled by orderAgg - }, - { - name: 'size', - default: 5, - }, - { - name: 'otherBucket', - default: false, - write: noop, - }, - { - name: 'otherBucketLabel', - type: 'string', - default: i18n.translate('data.search.aggs.buckets.terms.otherBucketLabel', { - defaultMessage: 'Other', - }), - displayName: i18n.translate('data.search.aggs.otherBucket.labelForOtherBucketLabel', { - defaultMessage: 'Label for other bucket', - }), - shouldShow: agg => agg.getParam('otherBucket'), - write: noop, - }, - { - name: 'missingBucket', - default: false, - write: noop, }, - { - name: 'missingBucketLabel', - default: i18n.translate('data.search.aggs.buckets.terms.missingBucketLabel', { - defaultMessage: 'Missing', - description: `Default label used in charts when documents are missing a field. - Visible when you create a chart with a terms aggregation and enable "Show missing values"`, - }), - type: 'string', - displayName: i18n.translate('data.search.aggs.otherBucket.labelForMissingValuesLabel', { - defaultMessage: 'Label for missing values', - }), - shouldShow: agg => agg.getParam('missingBucket'), - write: noop, - }, - { - name: 'exclude', - displayName: i18n.translate('data.search.aggs.buckets.terms.excludeLabel', { - defaultMessage: 'Exclude', - }), - type: 'string', - advanced: true, - shouldShow: isStringType, - ...migrateIncludeExcludeFormat, - }, - { - name: 'include', - displayName: i18n.translate('data.search.aggs.buckets.terms.includeLabel', { - defaultMessage: 'Include', - }), - type: 'string', - advanced: true, - shouldShow: isStringType, - ...migrateIncludeExcludeFormat, - }, - ], -}); + { getInternalStartServices } + ); diff --git a/src/plugins/data/public/search/aggs/index.test.ts b/src/plugins/data/public/search/aggs/index.test.ts index 8c0e47763c2956..419c3fdab1caf9 100644 --- a/src/plugins/data/public/search/aggs/index.test.ts +++ b/src/plugins/data/public/search/aggs/index.test.ts @@ -20,16 +20,22 @@ import { coreMock } from '../../../../../../src/core/public/mocks'; import { getAggTypes } from './index'; -import { isBucketAggType } from './buckets/_bucket_agg_type'; +import { isBucketAggType } from './buckets/bucket_agg_type'; import { isMetricAggType } from './metrics/metric_agg_type'; import { QueryStart } from '../../query'; +import { FieldFormatsStart } from '../../field_formats'; describe('AggTypesComponent', () => { - const core = coreMock.createSetup(); + const coreSetup = coreMock.createSetup(); + const coreStart = coreMock.createSetup(); + const aggTypes = getAggTypes({ - uiSettings: core.uiSettings, - notifications: core.notifications, + uiSettings: coreSetup.uiSettings, query: {} as QueryStart, + getInternalStartServices: () => ({ + notifications: coreStart.notifications, + fieldFormats: {} as FieldFormatsStart, + }), }); const { buckets, metrics } = aggTypes; diff --git a/src/plugins/data/public/search/aggs/metrics/avg.ts b/src/plugins/data/public/search/aggs/metrics/avg.ts index 008dede3e1985c..d53ce8d3fc489d 100644 --- a/src/plugins/data/public/search/aggs/metrics/avg.ts +++ b/src/plugins/data/public/search/aggs/metrics/avg.ts @@ -21,25 +21,37 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; +import { GetInternalStartServicesFn } from '../../../types'; const averageTitle = i18n.translate('data.search.aggs.metrics.averageTitle', { defaultMessage: 'Average', }); -export const avgMetricAgg = new MetricAggType({ - name: METRIC_TYPES.AVG, - title: averageTitle, - makeLabel: aggConfig => { - return i18n.translate('data.search.aggs.metrics.averageLabel', { - defaultMessage: 'Average {field}', - values: { field: aggConfig.getFieldDisplayName() }, - }); - }, - params: [ +export interface AvgMetricAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} + +export const getAvgMetricAgg = ({ getInternalStartServices }: AvgMetricAggDependencies) => { + return new MetricAggType( { - name: 'field', - type: 'field', - filterFieldTypes: KBN_FIELD_TYPES.NUMBER, + name: METRIC_TYPES.AVG, + title: averageTitle, + makeLabel: aggConfig => { + return i18n.translate('data.search.aggs.metrics.averageLabel', { + defaultMessage: 'Average {field}', + values: { field: aggConfig.getFieldDisplayName() }, + }); + }, + params: [ + { + name: 'field', + type: 'field', + filterFieldTypes: KBN_FIELD_TYPES.NUMBER, + }, + ], }, - ], -}); + { + getInternalStartServices, + } + ); +}; diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_avg.ts b/src/plugins/data/public/search/aggs/metrics/bucket_avg.ts index 11bb5592747297..2c32ebc671539c 100644 --- a/src/plugins/data/public/search/aggs/metrics/bucket_avg.ts +++ b/src/plugins/data/public/search/aggs/metrics/bucket_avg.ts @@ -23,6 +23,11 @@ import { MetricAggType } from './metric_agg_type'; import { makeNestedLabel } from './lib/make_nested_label'; import { siblingPipelineAggHelper } from './lib/sibling_pipeline_agg_helper'; import { METRIC_TYPES } from './metric_agg_types'; +import { GetInternalStartServicesFn } from '../../../types'; + +export interface BucketAvgMetricAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} const overallAverageLabel = i18n.translate('data.search.aggs.metrics.overallAverageLabel', { defaultMessage: 'overall average', @@ -32,25 +37,34 @@ const averageBucketTitle = i18n.translate('data.search.aggs.metrics.averageBucke defaultMessage: 'Average Bucket', }); -export const bucketAvgMetricAgg = new MetricAggType({ - name: METRIC_TYPES.AVG_BUCKET, - title: averageBucketTitle, - makeLabel: agg => makeNestedLabel(agg, overallAverageLabel), - subtype: siblingPipelineAggHelper.subtype, - params: [...siblingPipelineAggHelper.params()], - getFormat: siblingPipelineAggHelper.getFormat, - getValue(agg, bucket) { - const customMetric = agg.getParam('customMetric'); - const customBucket = agg.getParam('customBucket'); - const scaleMetrics = customMetric.type && customMetric.type.isScalable(); +export const getBucketAvgMetricAgg = ({ + getInternalStartServices, +}: BucketAvgMetricAggDependencies) => { + return new MetricAggType( + { + name: METRIC_TYPES.AVG_BUCKET, + title: averageBucketTitle, + makeLabel: agg => makeNestedLabel(agg, overallAverageLabel), + subtype: siblingPipelineAggHelper.subtype, + params: [...siblingPipelineAggHelper.params()], + getFormat: siblingPipelineAggHelper.getFormat, + getValue(agg, bucket) { + const customMetric = agg.getParam('customMetric'); + const customBucket = agg.getParam('customBucket'); + const scaleMetrics = customMetric.type && customMetric.type.isScalable(); - let value = bucket[agg.id] && bucket[agg.id].value; + let value = bucket[agg.id] && bucket[agg.id].value; - if (scaleMetrics && customBucket.type.name === 'date_histogram') { - const aggInfo = customBucket.write(); + if (scaleMetrics && customBucket.type.name === 'date_histogram') { + const aggInfo = customBucket.write(); - value *= get(aggInfo, 'bucketInterval.scale', 1); + value *= get(aggInfo, 'bucketInterval.scale', 1); + } + return value; + }, + }, + { + getInternalStartServices, } - return value; - }, -}); + ); +}; diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_max.ts b/src/plugins/data/public/search/aggs/metrics/bucket_max.ts index 0668a9bcf57a8f..1e57a2dd8e38e6 100644 --- a/src/plugins/data/public/search/aggs/metrics/bucket_max.ts +++ b/src/plugins/data/public/search/aggs/metrics/bucket_max.ts @@ -22,6 +22,11 @@ import { MetricAggType } from './metric_agg_type'; import { makeNestedLabel } from './lib/make_nested_label'; import { siblingPipelineAggHelper } from './lib/sibling_pipeline_agg_helper'; import { METRIC_TYPES } from './metric_agg_types'; +import { GetInternalStartServicesFn } from '../../../types'; + +export interface BucketMaxMetricAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} const overallMaxLabel = i18n.translate('data.search.aggs.metrics.overallMaxLabel', { defaultMessage: 'overall max', @@ -31,11 +36,20 @@ const maxBucketTitle = i18n.translate('data.search.aggs.metrics.maxBucketTitle', defaultMessage: 'Max Bucket', }); -export const bucketMaxMetricAgg = new MetricAggType({ - name: METRIC_TYPES.MAX_BUCKET, - title: maxBucketTitle, - makeLabel: agg => makeNestedLabel(agg, overallMaxLabel), - subtype: siblingPipelineAggHelper.subtype, - params: [...siblingPipelineAggHelper.params()], - getFormat: siblingPipelineAggHelper.getFormat, -}); +export const getBucketMaxMetricAgg = ({ + getInternalStartServices, +}: BucketMaxMetricAggDependencies) => { + return new MetricAggType( + { + name: METRIC_TYPES.MAX_BUCKET, + title: maxBucketTitle, + makeLabel: agg => makeNestedLabel(agg, overallMaxLabel), + subtype: siblingPipelineAggHelper.subtype, + params: [...siblingPipelineAggHelper.params()], + getFormat: siblingPipelineAggHelper.getFormat, + }, + { + getInternalStartServices, + } + ); +}; diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_min.ts b/src/plugins/data/public/search/aggs/metrics/bucket_min.ts index 8f728cb5e7e426..0484af23a71411 100644 --- a/src/plugins/data/public/search/aggs/metrics/bucket_min.ts +++ b/src/plugins/data/public/search/aggs/metrics/bucket_min.ts @@ -22,6 +22,11 @@ import { MetricAggType } from './metric_agg_type'; import { makeNestedLabel } from './lib/make_nested_label'; import { siblingPipelineAggHelper } from './lib/sibling_pipeline_agg_helper'; import { METRIC_TYPES } from './metric_agg_types'; +import { GetInternalStartServicesFn } from '../../../types'; + +export interface BucketMinMetricAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} const overallMinLabel = i18n.translate('data.search.aggs.metrics.overallMinLabel', { defaultMessage: 'overall min', @@ -31,11 +36,20 @@ const minBucketTitle = i18n.translate('data.search.aggs.metrics.minBucketTitle', defaultMessage: 'Min Bucket', }); -export const bucketMinMetricAgg = new MetricAggType({ - name: METRIC_TYPES.MIN_BUCKET, - title: minBucketTitle, - makeLabel: agg => makeNestedLabel(agg, overallMinLabel), - subtype: siblingPipelineAggHelper.subtype, - params: [...siblingPipelineAggHelper.params()], - getFormat: siblingPipelineAggHelper.getFormat, -}); +export const getBucketMinMetricAgg = ({ + getInternalStartServices, +}: BucketMinMetricAggDependencies) => { + return new MetricAggType( + { + name: METRIC_TYPES.MIN_BUCKET, + title: minBucketTitle, + makeLabel: agg => makeNestedLabel(agg, overallMinLabel), + subtype: siblingPipelineAggHelper.subtype, + params: [...siblingPipelineAggHelper.params()], + getFormat: siblingPipelineAggHelper.getFormat, + }, + { + getInternalStartServices, + } + ); +}; diff --git a/src/plugins/data/public/search/aggs/metrics/bucket_sum.ts b/src/plugins/data/public/search/aggs/metrics/bucket_sum.ts index 1f9392c5bec359..0a4d29a18a9802 100644 --- a/src/plugins/data/public/search/aggs/metrics/bucket_sum.ts +++ b/src/plugins/data/public/search/aggs/metrics/bucket_sum.ts @@ -22,6 +22,11 @@ import { MetricAggType } from './metric_agg_type'; import { makeNestedLabel } from './lib/make_nested_label'; import { siblingPipelineAggHelper } from './lib/sibling_pipeline_agg_helper'; import { METRIC_TYPES } from './metric_agg_types'; +import { GetInternalStartServicesFn } from '../../../types'; + +export interface BucketSumMetricAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} const overallSumLabel = i18n.translate('data.search.aggs.metrics.overallSumLabel', { defaultMessage: 'overall sum', @@ -31,11 +36,20 @@ const sumBucketTitle = i18n.translate('data.search.aggs.metrics.sumBucketTitle', defaultMessage: 'Sum Bucket', }); -export const bucketSumMetricAgg = new MetricAggType({ - name: METRIC_TYPES.SUM_BUCKET, - title: sumBucketTitle, - makeLabel: agg => makeNestedLabel(agg, overallSumLabel), - subtype: siblingPipelineAggHelper.subtype, - params: [...siblingPipelineAggHelper.params()], - getFormat: siblingPipelineAggHelper.getFormat, -}); +export const getBucketSumMetricAgg = ({ + getInternalStartServices, +}: BucketSumMetricAggDependencies) => { + return new MetricAggType( + { + name: METRIC_TYPES.SUM_BUCKET, + title: sumBucketTitle, + makeLabel: agg => makeNestedLabel(agg, overallSumLabel), + subtype: siblingPipelineAggHelper.subtype, + params: [...siblingPipelineAggHelper.params()], + getFormat: siblingPipelineAggHelper.getFormat, + }, + { + getInternalStartServices, + } + ); +}; diff --git a/src/plugins/data/public/search/aggs/metrics/cardinality.ts b/src/plugins/data/public/search/aggs/metrics/cardinality.ts index 88cdf3175665ee..10b6b5aff1abd3 100644 --- a/src/plugins/data/public/search/aggs/metrics/cardinality.ts +++ b/src/plugins/data/public/search/aggs/metrics/cardinality.ts @@ -18,36 +18,48 @@ */ import { i18n } from '@kbn/i18n'; -import { MetricAggType } from './metric_agg_type'; +import { MetricAggType, IMetricAggConfig } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; -import { getFieldFormats } from '../../../../public/services'; +import { GetInternalStartServicesFn } from '../../../types'; const uniqueCountTitle = i18n.translate('data.search.aggs.metrics.uniqueCountTitle', { defaultMessage: 'Unique Count', }); -export const cardinalityMetricAgg = new MetricAggType({ - name: METRIC_TYPES.CARDINALITY, - title: uniqueCountTitle, - makeLabel(aggConfig) { - return i18n.translate('data.search.aggs.metrics.uniqueCountLabel', { - defaultMessage: 'Unique count of {field}', - values: { field: aggConfig.getFieldDisplayName() }, - }); - }, - getFormat() { - const fieldFormatsService = getFieldFormats(); +export interface CardinalityMetricAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} - return fieldFormatsService.getDefaultInstance(KBN_FIELD_TYPES.NUMBER); - }, - params: [ +export const getCardinalityMetricAgg = ({ + getInternalStartServices, +}: CardinalityMetricAggDependencies) => + new MetricAggType( { - name: 'field', - type: 'field', - filterFieldTypes: Object.values(KBN_FIELD_TYPES).filter( - type => type !== KBN_FIELD_TYPES.HISTOGRAM - ), + name: METRIC_TYPES.CARDINALITY, + title: uniqueCountTitle, + makeLabel(aggConfig: IMetricAggConfig) { + return i18n.translate('data.search.aggs.metrics.uniqueCountLabel', { + defaultMessage: 'Unique count of {field}', + values: { field: aggConfig.getFieldDisplayName() }, + }); + }, + getFormat() { + const { fieldFormats } = getInternalStartServices(); + + return fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.NUMBER); + }, + params: [ + { + name: 'field', + type: 'field', + filterFieldTypes: Object.values(KBN_FIELD_TYPES).filter( + type => type !== KBN_FIELD_TYPES.HISTOGRAM + ), + }, + ], }, - ], -}); + { + getInternalStartServices, + } + ); diff --git a/src/plugins/data/public/search/aggs/metrics/count.ts b/src/plugins/data/public/search/aggs/metrics/count.ts index 3ec1e18d66ab98..bd0b83798c7db6 100644 --- a/src/plugins/data/public/search/aggs/metrics/count.ts +++ b/src/plugins/data/public/search/aggs/metrics/count.ts @@ -21,28 +21,38 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; -import { getFieldFormats } from '../../../../public/services'; +import { GetInternalStartServicesFn } from '../../../types'; -export const countMetricAgg = new MetricAggType({ - name: METRIC_TYPES.COUNT, - title: i18n.translate('data.search.aggs.metrics.countTitle', { - defaultMessage: 'Count', - }), - hasNoDsl: true, - makeLabel() { - return i18n.translate('data.search.aggs.metrics.countLabel', { - defaultMessage: 'Count', - }); - }, - getFormat() { - const fieldFormatsService = getFieldFormats(); +export interface CountMetricAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} - return fieldFormatsService.getDefaultInstance(KBN_FIELD_TYPES.NUMBER); - }, - getValue(agg, bucket) { - return bucket.doc_count; - }, - isScalable() { - return true; - }, -}); +export const getCountMetricAgg = ({ getInternalStartServices }: CountMetricAggDependencies) => + new MetricAggType( + { + name: METRIC_TYPES.COUNT, + title: i18n.translate('data.search.aggs.metrics.countTitle', { + defaultMessage: 'Count', + }), + hasNoDsl: true, + makeLabel() { + return i18n.translate('data.search.aggs.metrics.countLabel', { + defaultMessage: 'Count', + }); + }, + getFormat() { + const { fieldFormats } = getInternalStartServices(); + + return fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.NUMBER); + }, + getValue(agg, bucket) { + return bucket.doc_count; + }, + isScalable() { + return true; + }, + }, + { + getInternalStartServices, + } + ); diff --git a/src/plugins/data/public/search/aggs/metrics/cumulative_sum.ts b/src/plugins/data/public/search/aggs/metrics/cumulative_sum.ts index a5d02459900bb0..8ca922e144a1f6 100644 --- a/src/plugins/data/public/search/aggs/metrics/cumulative_sum.ts +++ b/src/plugins/data/public/search/aggs/metrics/cumulative_sum.ts @@ -22,6 +22,11 @@ import { MetricAggType } from './metric_agg_type'; import { parentPipelineAggHelper } from './lib/parent_pipeline_agg_helper'; import { makeNestedLabel } from './lib/make_nested_label'; import { METRIC_TYPES } from './metric_agg_types'; +import { GetInternalStartServicesFn } from '../../../types'; + +export interface CumulativeSumMetricAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} const cumulativeSumLabel = i18n.translate('data.search.aggs.metrics.cumulativeSumLabel', { defaultMessage: 'cumulative sum', @@ -31,11 +36,20 @@ const cumulativeSumTitle = i18n.translate('data.search.aggs.metrics.cumulativeSu defaultMessage: 'Cumulative Sum', }); -export const cumulativeSumMetricAgg = new MetricAggType({ - name: METRIC_TYPES.CUMULATIVE_SUM, - title: cumulativeSumTitle, - subtype: parentPipelineAggHelper.subtype, - makeLabel: agg => makeNestedLabel(agg, cumulativeSumLabel), - params: [...parentPipelineAggHelper.params()], - getFormat: parentPipelineAggHelper.getFormat, -}); +export const getCumulativeSumMetricAgg = ({ + getInternalStartServices, +}: CumulativeSumMetricAggDependencies) => { + return new MetricAggType( + { + name: METRIC_TYPES.CUMULATIVE_SUM, + title: cumulativeSumTitle, + subtype: parentPipelineAggHelper.subtype, + makeLabel: agg => makeNestedLabel(agg, cumulativeSumLabel), + params: [...parentPipelineAggHelper.params()], + getFormat: parentPipelineAggHelper.getFormat, + }, + { + getInternalStartServices, + } + ); +}; diff --git a/src/plugins/data/public/search/aggs/metrics/derivative.ts b/src/plugins/data/public/search/aggs/metrics/derivative.ts index 1169a527b0668c..5752a72c846aa2 100644 --- a/src/plugins/data/public/search/aggs/metrics/derivative.ts +++ b/src/plugins/data/public/search/aggs/metrics/derivative.ts @@ -22,6 +22,11 @@ import { MetricAggType } from './metric_agg_type'; import { parentPipelineAggHelper } from './lib/parent_pipeline_agg_helper'; import { makeNestedLabel } from './lib/make_nested_label'; import { METRIC_TYPES } from './metric_agg_types'; +import { GetInternalStartServicesFn } from '../../../types'; + +export interface DerivativeMetricAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} const derivativeLabel = i18n.translate('data.search.aggs.metrics.derivativeLabel', { defaultMessage: 'derivative', @@ -31,13 +36,22 @@ const derivativeTitle = i18n.translate('data.search.aggs.metrics.derivativeTitle defaultMessage: 'Derivative', }); -export const derivativeMetricAgg = new MetricAggType({ - name: METRIC_TYPES.DERIVATIVE, - title: derivativeTitle, - subtype: parentPipelineAggHelper.subtype, - makeLabel(agg) { - return makeNestedLabel(agg, derivativeLabel); - }, - params: [...parentPipelineAggHelper.params()], - getFormat: parentPipelineAggHelper.getFormat, -}); +export const getDerivativeMetricAgg = ({ + getInternalStartServices, +}: DerivativeMetricAggDependencies) => { + return new MetricAggType( + { + name: METRIC_TYPES.DERIVATIVE, + title: derivativeTitle, + subtype: parentPipelineAggHelper.subtype, + makeLabel(agg) { + return makeNestedLabel(agg, derivativeLabel); + }, + params: [...parentPipelineAggHelper.params()], + getFormat: parentPipelineAggHelper.getFormat, + }, + { + getInternalStartServices, + } + ); +}; diff --git a/src/plugins/data/public/search/aggs/metrics/geo_bounds.ts b/src/plugins/data/public/search/aggs/metrics/geo_bounds.ts index 8a9f66f4b22a8a..00927ebba56bfa 100644 --- a/src/plugins/data/public/search/aggs/metrics/geo_bounds.ts +++ b/src/plugins/data/public/search/aggs/metrics/geo_bounds.ts @@ -21,6 +21,11 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; +import { GetInternalStartServicesFn } from '../../../types'; + +export interface GeoBoundsMetricAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} const geoBoundsTitle = i18n.translate('data.search.aggs.metrics.geoBoundsTitle', { defaultMessage: 'Geo Bounds', @@ -30,15 +35,24 @@ const geoBoundsLabel = i18n.translate('data.search.aggs.metrics.geoBoundsLabel', defaultMessage: 'Geo Bounds', }); -export const geoBoundsMetricAgg = new MetricAggType({ - name: METRIC_TYPES.GEO_BOUNDS, - title: geoBoundsTitle, - makeLabel: () => geoBoundsLabel, - params: [ +export const getGeoBoundsMetricAgg = ({ + getInternalStartServices, +}: GeoBoundsMetricAggDependencies) => { + return new MetricAggType( { - name: 'field', - type: 'field', - filterFieldTypes: KBN_FIELD_TYPES.GEO_POINT, + name: METRIC_TYPES.GEO_BOUNDS, + title: geoBoundsTitle, + makeLabel: () => geoBoundsLabel, + params: [ + { + name: 'field', + type: 'field', + filterFieldTypes: KBN_FIELD_TYPES.GEO_POINT, + }, + ], }, - ], -}); + { + getInternalStartServices, + } + ); +}; diff --git a/src/plugins/data/public/search/aggs/metrics/geo_centroid.ts b/src/plugins/data/public/search/aggs/metrics/geo_centroid.ts index a4e4413843bdd6..a4b084f794a5de 100644 --- a/src/plugins/data/public/search/aggs/metrics/geo_centroid.ts +++ b/src/plugins/data/public/search/aggs/metrics/geo_centroid.ts @@ -21,6 +21,11 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; +import { GetInternalStartServicesFn } from '../../../types'; + +export interface GeoCentroidMetricAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} const geoCentroidTitle = i18n.translate('data.search.aggs.metrics.geoCentroidTitle', { defaultMessage: 'Geo Centroid', @@ -30,18 +35,27 @@ const geoCentroidLabel = i18n.translate('data.search.aggs.metrics.geoCentroidLab defaultMessage: 'Geo Centroid', }); -export const geoCentroidMetricAgg = new MetricAggType({ - name: METRIC_TYPES.GEO_CENTROID, - title: geoCentroidTitle, - makeLabel: () => geoCentroidLabel, - params: [ +export const getGeoCentroidMetricAgg = ({ + getInternalStartServices, +}: GeoCentroidMetricAggDependencies) => { + return new MetricAggType( { - name: 'field', - type: 'field', - filterFieldTypes: KBN_FIELD_TYPES.GEO_POINT, + name: METRIC_TYPES.GEO_CENTROID, + title: geoCentroidTitle, + makeLabel: () => geoCentroidLabel, + params: [ + { + name: 'field', + type: 'field', + filterFieldTypes: KBN_FIELD_TYPES.GEO_POINT, + }, + ], + getValue(agg, bucket) { + return bucket[agg.id] && bucket[agg.id].location; + }, }, - ], - getValue(agg, bucket) { - return bucket[agg.id] && bucket[agg.id].location; - }, -}); + { + getInternalStartServices, + } + ); +}; diff --git a/src/plugins/data/public/search/aggs/metrics/max.ts b/src/plugins/data/public/search/aggs/metrics/max.ts index 0cfb7be699a95c..88e8b485cb73f9 100644 --- a/src/plugins/data/public/search/aggs/metrics/max.ts +++ b/src/plugins/data/public/search/aggs/metrics/max.ts @@ -21,25 +21,37 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; +import { GetInternalStartServicesFn } from '../../../types'; const maxTitle = i18n.translate('data.search.aggs.metrics.maxTitle', { defaultMessage: 'Max', }); -export const maxMetricAgg = new MetricAggType({ - name: METRIC_TYPES.MAX, - title: maxTitle, - makeLabel(aggConfig) { - return i18n.translate('data.search.aggs.metrics.maxLabel', { - defaultMessage: 'Max {field}', - values: { field: aggConfig.getFieldDisplayName() }, - }); - }, - params: [ +export interface MaxMetricAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} + +export const getMaxMetricAgg = ({ getInternalStartServices }: MaxMetricAggDependencies) => { + return new MetricAggType( { - name: 'field', - type: 'field', - filterFieldTypes: [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.DATE], + name: METRIC_TYPES.MAX, + title: maxTitle, + makeLabel(aggConfig) { + return i18n.translate('data.search.aggs.metrics.maxLabel', { + defaultMessage: 'Max {field}', + values: { field: aggConfig.getFieldDisplayName() }, + }); + }, + params: [ + { + name: 'field', + type: 'field', + filterFieldTypes: [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.DATE], + }, + ], }, - ], -}); + { + getInternalStartServices, + } + ); +}; diff --git a/src/plugins/data/public/search/aggs/metrics/median.test.ts b/src/plugins/data/public/search/aggs/metrics/median.test.ts index ad55837ec9a300..f80c46026f50ab 100644 --- a/src/plugins/data/public/search/aggs/metrics/median.test.ts +++ b/src/plugins/data/public/search/aggs/metrics/median.test.ts @@ -17,16 +17,24 @@ * under the License. */ -import { medianMetricAgg } from './median'; +import { getMedianMetricAgg, MedianMetricAggDependencies } from './median'; import { AggConfigs, IAggConfigs } from '../agg_configs'; import { mockAggTypesRegistry } from '../test_helpers'; import { METRIC_TYPES } from './metric_agg_types'; +import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; +import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; describe('AggTypeMetricMedianProvider class', () => { let aggConfigs: IAggConfigs; + const aggTypesDependencies: MedianMetricAggDependencies = { + getInternalStartServices: () => ({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + }), + }; beforeEach(() => { - const typesRegistry = mockAggTypesRegistry([medianMetricAgg]); + const typesRegistry = mockAggTypesRegistry([getMedianMetricAgg(aggTypesDependencies)]); const field = { name: 'bytes', }; diff --git a/src/plugins/data/public/search/aggs/metrics/median.ts b/src/plugins/data/public/search/aggs/metrics/median.ts index faa0694cd53120..a398f017602b07 100644 --- a/src/plugins/data/public/search/aggs/metrics/median.ts +++ b/src/plugins/data/public/search/aggs/metrics/median.ts @@ -21,33 +21,49 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; +import { GetInternalStartServicesFn } from '../../../types'; const medianTitle = i18n.translate('data.search.aggs.metrics.medianTitle', { defaultMessage: 'Median', }); -export const medianMetricAgg = new MetricAggType({ - name: METRIC_TYPES.MEDIAN, - dslName: 'percentiles', - title: medianTitle, - makeLabel(aggConfig) { - return i18n.translate('data.search.aggs.metrics.medianLabel', { - defaultMessage: 'Median {field}', - values: { field: aggConfig.getFieldDisplayName() }, - }); - }, - params: [ +export interface MedianMetricAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} + +export const getMedianMetricAgg = ({ getInternalStartServices }: MedianMetricAggDependencies) => { + return new MetricAggType( { - name: 'field', - type: 'field', - filterFieldTypes: [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.DATE, KBN_FIELD_TYPES.HISTOGRAM], - write(agg, output) { - output.params.field = agg.getParam('field').name; - output.params.percents = [50]; + name: METRIC_TYPES.MEDIAN, + dslName: 'percentiles', + title: medianTitle, + makeLabel(aggConfig) { + return i18n.translate('data.search.aggs.metrics.medianLabel', { + defaultMessage: 'Median {field}', + values: { field: aggConfig.getFieldDisplayName() }, + }); + }, + params: [ + { + name: 'field', + type: 'field', + filterFieldTypes: [ + KBN_FIELD_TYPES.NUMBER, + KBN_FIELD_TYPES.DATE, + KBN_FIELD_TYPES.HISTOGRAM, + ], + write(agg, output) { + output.params.field = agg.getParam('field').name; + output.params.percents = [50]; + }, + }, + ], + getValue(agg, bucket) { + return bucket[agg.id].values['50.0']; }, }, - ], - getValue(agg, bucket) { - return bucket[agg.id].values['50.0']; - }, -}); + { + getInternalStartServices, + } + ); +}; diff --git a/src/plugins/data/public/search/aggs/metrics/metric_agg_type.ts b/src/plugins/data/public/search/aggs/metrics/metric_agg_type.ts index 05c4cb3de4bdf4..bb16cba1bee623 100644 --- a/src/plugins/data/public/search/aggs/metrics/metric_agg_type.ts +++ b/src/plugins/data/public/search/aggs/metrics/metric_agg_type.ts @@ -23,8 +23,8 @@ import { AggParamType } from '../param_types/agg'; import { AggConfig } from '../agg_config'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; -import { getFieldFormats } from '../../../../public/services'; import { FieldTypes } from '../param_types'; +import { GetInternalStartServicesFn } from '../../../types'; export interface IMetricAggConfig extends AggConfig { type: InstanceType; @@ -44,6 +44,10 @@ interface MetricAggTypeConfig subtype?: string; } +interface MetricAggTypeDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} + // TODO need to make a more explicit interface for this export type IMetricAggType = MetricAggType; @@ -57,8 +61,11 @@ export class MetricAggType {}; - constructor(config: MetricAggTypeConfig) { - super(config); + constructor( + config: MetricAggTypeConfig, + dependencies: MetricAggTypeDependencies + ) { + super(config, dependencies); this.getValue = config.getValue || @@ -78,11 +85,9 @@ export class MetricAggType { - const fieldFormatsService = getFieldFormats(); + const { fieldFormats } = dependencies.getInternalStartServices(); const field = agg.getField(); - return field - ? field.format - : fieldFormatsService.getDefaultInstance(KBN_FIELD_TYPES.NUMBER); + return field ? field.format : fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.NUMBER); }); this.subtype = diff --git a/src/plugins/data/public/search/aggs/metrics/min.ts b/src/plugins/data/public/search/aggs/metrics/min.ts index 0a9abf1edcd043..aae16f357186cb 100644 --- a/src/plugins/data/public/search/aggs/metrics/min.ts +++ b/src/plugins/data/public/search/aggs/metrics/min.ts @@ -21,25 +21,37 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; +import { GetInternalStartServicesFn } from '../../../types'; const minTitle = i18n.translate('data.search.aggs.metrics.minTitle', { defaultMessage: 'Min', }); -export const minMetricAgg = new MetricAggType({ - name: METRIC_TYPES.MIN, - title: minTitle, - makeLabel(aggConfig) { - return i18n.translate('data.search.aggs.metrics.minLabel', { - defaultMessage: 'Min {field}', - values: { field: aggConfig.getFieldDisplayName() }, - }); - }, - params: [ +export interface MinMetricAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} + +export const getMinMetricAgg = ({ getInternalStartServices }: MinMetricAggDependencies) => { + return new MetricAggType( { - name: 'field', - type: 'field', - filterFieldTypes: [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.DATE], + name: METRIC_TYPES.MIN, + title: minTitle, + makeLabel(aggConfig) { + return i18n.translate('data.search.aggs.metrics.minLabel', { + defaultMessage: 'Min {field}', + values: { field: aggConfig.getFieldDisplayName() }, + }); + }, + params: [ + { + name: 'field', + type: 'field', + filterFieldTypes: [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.DATE], + }, + ], }, - ], -}); + { + getInternalStartServices, + } + ); +}; diff --git a/src/plugins/data/public/search/aggs/metrics/moving_avg.ts b/src/plugins/data/public/search/aggs/metrics/moving_avg.ts index cb733507858bca..94b9b1d8cd4875 100644 --- a/src/plugins/data/public/search/aggs/metrics/moving_avg.ts +++ b/src/plugins/data/public/search/aggs/metrics/moving_avg.ts @@ -22,6 +22,11 @@ import { MetricAggType } from './metric_agg_type'; import { parentPipelineAggHelper } from './lib/parent_pipeline_agg_helper'; import { makeNestedLabel } from './lib/make_nested_label'; import { METRIC_TYPES } from './metric_agg_types'; +import { GetInternalStartServicesFn } from '../../../types'; + +export interface MovingAvgMetricAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} const movingAvgTitle = i18n.translate('data.search.aggs.metrics.movingAvgTitle', { defaultMessage: 'Moving Avg', @@ -31,34 +36,43 @@ const movingAvgLabel = i18n.translate('data.search.aggs.metrics.movingAvgLabel', defaultMessage: 'moving avg', }); -export const movingAvgMetricAgg = new MetricAggType({ - name: METRIC_TYPES.MOVING_FN, - dslName: 'moving_fn', - title: movingAvgTitle, - subtype: parentPipelineAggHelper.subtype, - makeLabel: agg => makeNestedLabel(agg, movingAvgLabel), - params: [ - ...parentPipelineAggHelper.params(), +export const getMovingAvgMetricAgg = ({ + getInternalStartServices, +}: MovingAvgMetricAggDependencies) => { + return new MetricAggType( { - name: 'window', - default: 5, + name: METRIC_TYPES.MOVING_FN, + dslName: 'moving_fn', + title: movingAvgTitle, + subtype: parentPipelineAggHelper.subtype, + makeLabel: agg => makeNestedLabel(agg, movingAvgLabel), + params: [ + ...parentPipelineAggHelper.params(), + { + name: 'window', + default: 5, + }, + { + name: 'script', + default: 'MovingFunctions.unweightedAvg(values)', + }, + ], + getValue(agg, bucket) { + /** + * The previous implementation using `moving_avg` did not + * return any bucket in case there are no documents or empty window. + * The `moving_fn` aggregation returns buckets with the value null if the + * window is empty or doesn't return any value if the sibiling metric + * is null. Since our generic MetricAggType.getValue implementation + * would return the value 0 for null buckets, we need a specific + * implementation here, that preserves the null value. + */ + return bucket[agg.id] ? bucket[agg.id].value : null; + }, + getFormat: parentPipelineAggHelper.getFormat, }, { - name: 'script', - default: 'MovingFunctions.unweightedAvg(values)', - }, - ], - getValue(agg, bucket) { - /** - * The previous implementation using `moving_avg` did not - * return any bucket in case there are no documents or empty window. - * The `moving_fn` aggregation returns buckets with the value null if the - * window is empty or doesn't return any value if the sibiling metric - * is null. Since our generic MetricAggType.getValue implementation - * would return the value 0 for null buckets, we need a specific - * implementation here, that preserves the null value. - */ - return bucket[agg.id] ? bucket[agg.id].value : null; - }, - getFormat: parentPipelineAggHelper.getFormat, -}); + getInternalStartServices, + } + ); +}; diff --git a/src/plugins/data/public/search/aggs/metrics/parent_pipeline.test.ts b/src/plugins/data/public/search/aggs/metrics/parent_pipeline.test.ts index 02e63f653f94f2..af983a50f6c234 100644 --- a/src/plugins/data/public/search/aggs/metrics/parent_pipeline.test.ts +++ b/src/plugins/data/public/search/aggs/metrics/parent_pipeline.test.ts @@ -17,26 +17,47 @@ * under the License. */ -import { derivativeMetricAgg } from './derivative'; -import { cumulativeSumMetricAgg } from './cumulative_sum'; -import { movingAvgMetricAgg } from './moving_avg'; -import { serialDiffMetricAgg } from './serial_diff'; +import { getDerivativeMetricAgg } from './derivative'; +import { getCumulativeSumMetricAgg } from './cumulative_sum'; +import { getMovingAvgMetricAgg } from './moving_avg'; +import { getSerialDiffMetricAgg } from './serial_diff'; import { AggConfigs } from '../agg_configs'; -import { mockDataServices, mockAggTypesRegistry } from '../test_helpers'; +import { mockAggTypesRegistry } from '../test_helpers'; import { IMetricAggConfig, MetricAggType } from './metric_agg_type'; +import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; +import { GetInternalStartServicesFn } from '../../../types'; +import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; describe('parent pipeline aggs', function() { - beforeEach(() => { - mockDataServices(); + const getInternalStartServices: GetInternalStartServicesFn = () => ({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), }); const typesRegistry = mockAggTypesRegistry(); const metrics = [ - { name: 'derivative', title: 'Derivative', provider: derivativeMetricAgg }, - { name: 'cumulative_sum', title: 'Cumulative Sum', provider: cumulativeSumMetricAgg }, - { name: 'moving_avg', title: 'Moving Avg', provider: movingAvgMetricAgg, dslName: 'moving_fn' }, - { name: 'serial_diff', title: 'Serial Diff', provider: serialDiffMetricAgg }, + { + name: 'derivative', + title: 'Derivative', + provider: getDerivativeMetricAgg({ getInternalStartServices }), + }, + { + name: 'cumulative_sum', + title: 'Cumulative Sum', + provider: getCumulativeSumMetricAgg({ getInternalStartServices }), + }, + { + name: 'moving_avg', + title: 'Moving Avg', + provider: getMovingAvgMetricAgg({ getInternalStartServices }), + dslName: 'moving_fn', + }, + { + name: 'serial_diff', + title: 'Serial Diff', + provider: getSerialDiffMetricAgg({ getInternalStartServices }), + }, ]; metrics.forEach(metric => { diff --git a/src/plugins/data/public/search/aggs/metrics/percentile_ranks.test.ts b/src/plugins/data/public/search/aggs/metrics/percentile_ranks.test.ts index 628f1cd204ee5e..2944fc8c11b230 100644 --- a/src/plugins/data/public/search/aggs/metrics/percentile_ranks.test.ts +++ b/src/plugins/data/public/search/aggs/metrics/percentile_ranks.test.ts @@ -17,18 +17,28 @@ * under the License. */ -import { IPercentileRanksAggConfig, percentileRanksMetricAgg } from './percentile_ranks'; +import { + IPercentileRanksAggConfig, + getPercentileRanksMetricAgg, + PercentileRanksMetricAggDependencies, +} from './percentile_ranks'; import { AggConfigs, IAggConfigs } from '../agg_configs'; -import { mockDataServices, mockAggTypesRegistry } from '../test_helpers'; +import { mockAggTypesRegistry } from '../test_helpers'; import { METRIC_TYPES } from './metric_agg_types'; +import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; +import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; describe('AggTypesMetricsPercentileRanksProvider class', function() { let aggConfigs: IAggConfigs; + const aggTypesDependencies: PercentileRanksMetricAggDependencies = { + getInternalStartServices: () => ({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + }), + }; beforeEach(() => { - mockDataServices(); - - const typesRegistry = mockAggTypesRegistry([percentileRanksMetricAgg]); + const typesRegistry = mockAggTypesRegistry([getPercentileRanksMetricAgg(aggTypesDependencies)]); const field = { name: 'bytes', }; @@ -65,7 +75,7 @@ describe('AggTypesMetricsPercentileRanksProvider class', function() { }); it('uses the custom label if it is set', function() { - const responseAggs: any = percentileRanksMetricAgg.getResponseAggs( + const responseAggs: any = getPercentileRanksMetricAgg(aggTypesDependencies).getResponseAggs( aggConfigs.aggs[0] as IPercentileRanksAggConfig ); diff --git a/src/plugins/data/public/search/aggs/metrics/percentile_ranks.ts b/src/plugins/data/public/search/aggs/metrics/percentile_ranks.ts index 7dc0f70ea7b80a..0d79665ff9c4e1 100644 --- a/src/plugins/data/public/search/aggs/metrics/percentile_ranks.ts +++ b/src/plugins/data/public/search/aggs/metrics/percentile_ranks.ts @@ -23,68 +23,86 @@ import { getResponseAggConfigClass, IResponseAggConfig } from './lib/get_respons import { getPercentileValue } from './percentiles_get_value'; import { METRIC_TYPES } from './metric_agg_types'; import { FIELD_FORMAT_IDS, KBN_FIELD_TYPES } from '../../../../common'; -import { getFieldFormats } from '../../../../public/services'; +import { GetInternalStartServicesFn } from '../../../types'; // required by the values editor export type IPercentileRanksAggConfig = IResponseAggConfig; -const valueProps = { - makeLabel(this: IPercentileRanksAggConfig) { - const fieldFormatsService = getFieldFormats(); - const field = this.getField(); - const format = - (field && field.format) || fieldFormatsService.getDefaultInstance(KBN_FIELD_TYPES.NUMBER); - const customLabel = this.getParam('customLabel'); - const label = customLabel || this.getFieldDisplayName(); +export interface PercentileRanksMetricAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} - return i18n.translate('data.search.aggs.metrics.percentileRanks.valuePropsLabel', { - defaultMessage: 'Percentile rank {format} of "{label}"', - values: { format: format.convert(this.key, 'text'), label }, - }); - }, -}; +const getValueProps = (getInternalStartServices: GetInternalStartServicesFn) => { + return { + makeLabel(this: IPercentileRanksAggConfig) { + const { fieldFormats } = getInternalStartServices(); + const field = this.getField(); + const format = + (field && field.format) || fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.NUMBER); + const customLabel = this.getParam('customLabel'); + const label = customLabel || this.getFieldDisplayName(); -export const percentileRanksMetricAgg = new MetricAggType({ - name: METRIC_TYPES.PERCENTILE_RANKS, - title: i18n.translate('data.search.aggs.metrics.percentileRanksTitle', { - defaultMessage: 'Percentile Ranks', - }), - makeLabel(agg) { - return i18n.translate('data.search.aggs.metrics.percentileRanksLabel', { - defaultMessage: 'Percentile ranks of {field}', - values: { field: agg.getFieldDisplayName() }, - }); - }, - params: [ - { - name: 'field', - type: 'field', - filterFieldTypes: [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.HISTOGRAM], - }, - { - name: 'values', - default: [], + return i18n.translate('data.search.aggs.metrics.percentileRanks.valuePropsLabel', { + defaultMessage: 'Percentile rank {format} of "{label}"', + values: { format: format.convert(this.key, 'text'), label }, + }); }, + }; +}; + +export const getPercentileRanksMetricAgg = ({ + getInternalStartServices, +}: PercentileRanksMetricAggDependencies) => { + return new MetricAggType( { - write(agg, output) { - output.params.keyed = false; + name: METRIC_TYPES.PERCENTILE_RANKS, + title: i18n.translate('data.search.aggs.metrics.percentileRanksTitle', { + defaultMessage: 'Percentile Ranks', + }), + makeLabel(agg) { + return i18n.translate('data.search.aggs.metrics.percentileRanksLabel', { + defaultMessage: 'Percentile ranks of {field}', + values: { field: agg.getFieldDisplayName() }, + }); }, - }, - ], - getResponseAggs(agg) { - const ValueAggConfig = getResponseAggConfigClass(agg, valueProps); - const values = agg.getParam('values'); + params: [ + { + name: 'field', + type: 'field', + filterFieldTypes: [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.HISTOGRAM], + }, + { + name: 'values', + default: [], + }, + { + write(agg, output) { + output.params.keyed = false; + }, + }, + ], + getResponseAggs(agg) { + const ValueAggConfig = getResponseAggConfigClass( + agg, + getValueProps(getInternalStartServices) + ); + const values = agg.getParam('values'); - return values.map((value: any) => new ValueAggConfig(value)); - }, - getFormat() { - const fieldFormatsService = getFieldFormats(); - return ( - fieldFormatsService.getInstance(FIELD_FORMAT_IDS.PERCENT) || - fieldFormatsService.getDefaultInstance(KBN_FIELD_TYPES.NUMBER) - ); - }, - getValue(agg, bucket) { - return getPercentileValue(agg, bucket) / 100; - }, -}); + return values.map((value: any) => new ValueAggConfig(value)); + }, + getFormat() { + const { fieldFormats } = getInternalStartServices(); + return ( + fieldFormats.getInstance(FIELD_FORMAT_IDS.PERCENT) || + fieldFormats.getDefaultInstance(KBN_FIELD_TYPES.NUMBER) + ); + }, + getValue(agg, bucket) { + return getPercentileValue(agg, bucket) / 100; + }, + }, + { + getInternalStartServices, + } + ); +}; diff --git a/src/plugins/data/public/search/aggs/metrics/percentiles.test.ts b/src/plugins/data/public/search/aggs/metrics/percentiles.test.ts index e077bc0f8c7737..33bd42df74cc7a 100644 --- a/src/plugins/data/public/search/aggs/metrics/percentiles.test.ts +++ b/src/plugins/data/public/search/aggs/metrics/percentiles.test.ts @@ -17,16 +17,28 @@ * under the License. */ -import { IPercentileAggConfig, percentilesMetricAgg } from './percentiles'; +import { + IPercentileAggConfig, + getPercentilesMetricAgg, + PercentilesMetricAggDependencies, +} from './percentiles'; import { AggConfigs, IAggConfigs } from '../agg_configs'; import { mockAggTypesRegistry } from '../test_helpers'; import { METRIC_TYPES } from './metric_agg_types'; +import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; +import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; describe('AggTypesMetricsPercentilesProvider class', () => { let aggConfigs: IAggConfigs; + const aggTypesDependencies: PercentilesMetricAggDependencies = { + getInternalStartServices: () => ({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + }), + }; beforeEach(() => { - const typesRegistry = mockAggTypesRegistry([percentilesMetricAgg]); + const typesRegistry = mockAggTypesRegistry([getPercentilesMetricAgg(aggTypesDependencies)]); const field = { name: 'bytes', }; @@ -63,7 +75,7 @@ describe('AggTypesMetricsPercentilesProvider class', () => { }); it('uses the custom label if it is set', () => { - const responseAggs: any = percentilesMetricAgg.getResponseAggs( + const responseAggs: any = getPercentilesMetricAgg(aggTypesDependencies).getResponseAggs( aggConfigs.aggs[0] as IPercentileAggConfig ); diff --git a/src/plugins/data/public/search/aggs/metrics/percentiles.ts b/src/plugins/data/public/search/aggs/metrics/percentiles.ts index a39d68248d608c..040a52588dd945 100644 --- a/src/plugins/data/public/search/aggs/metrics/percentiles.ts +++ b/src/plugins/data/public/search/aggs/metrics/percentiles.ts @@ -24,9 +24,14 @@ import { KBN_FIELD_TYPES } from '../../../../common'; import { getResponseAggConfigClass, IResponseAggConfig } from './lib/get_response_agg_config_class'; import { getPercentileValue } from './percentiles_get_value'; import { ordinalSuffix } from './lib/ordinal_suffix'; +import { GetInternalStartServicesFn } from '../../../types'; export type IPercentileAggConfig = IResponseAggConfig; +export interface PercentilesMetricAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} + const valueProps = { makeLabel(this: IPercentileAggConfig) { const customLabel = this.getParam('customLabel'); @@ -39,38 +44,51 @@ const valueProps = { }, }; -export const percentilesMetricAgg = new MetricAggType({ - name: METRIC_TYPES.PERCENTILES, - title: i18n.translate('data.search.aggs.metrics.percentilesTitle', { - defaultMessage: 'Percentiles', - }), - makeLabel(agg) { - return i18n.translate('data.search.aggs.metrics.percentilesLabel', { - defaultMessage: 'Percentiles of {field}', - values: { field: agg.getFieldDisplayName() }, - }); - }, - params: [ - { - name: 'field', - type: 'field', - filterFieldTypes: [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.DATE, KBN_FIELD_TYPES.HISTOGRAM], - }, - { - name: 'percents', - default: [1, 5, 25, 50, 75, 95, 99], - }, +export const getPercentilesMetricAgg = ({ + getInternalStartServices, +}: PercentilesMetricAggDependencies) => { + return new MetricAggType( { - write(agg, output) { - output.params.keyed = false; + name: METRIC_TYPES.PERCENTILES, + title: i18n.translate('data.search.aggs.metrics.percentilesTitle', { + defaultMessage: 'Percentiles', + }), + makeLabel(agg) { + return i18n.translate('data.search.aggs.metrics.percentilesLabel', { + defaultMessage: 'Percentiles of {field}', + values: { field: agg.getFieldDisplayName() }, + }); }, - }, - ], - getResponseAggs(agg) { - const ValueAggConfig = getResponseAggConfigClass(agg, valueProps); + params: [ + { + name: 'field', + type: 'field', + filterFieldTypes: [ + KBN_FIELD_TYPES.NUMBER, + KBN_FIELD_TYPES.DATE, + KBN_FIELD_TYPES.HISTOGRAM, + ], + }, + { + name: 'percents', + default: [1, 5, 25, 50, 75, 95, 99], + }, + { + write(agg, output) { + output.params.keyed = false; + }, + }, + ], + getResponseAggs(agg) { + const ValueAggConfig = getResponseAggConfigClass(agg, valueProps); - return agg.getParam('percents').map((percent: any) => new ValueAggConfig(percent)); - }, + return agg.getParam('percents').map((percent: any) => new ValueAggConfig(percent)); + }, - getValue: getPercentileValue, -}); + getValue: getPercentileValue, + }, + { + getInternalStartServices, + } + ); +}; diff --git a/src/plugins/data/public/search/aggs/metrics/serial_diff.ts b/src/plugins/data/public/search/aggs/metrics/serial_diff.ts index 5af6e1952d1357..2b1498560f862e 100644 --- a/src/plugins/data/public/search/aggs/metrics/serial_diff.ts +++ b/src/plugins/data/public/search/aggs/metrics/serial_diff.ts @@ -22,6 +22,11 @@ import { MetricAggType } from './metric_agg_type'; import { parentPipelineAggHelper } from './lib/parent_pipeline_agg_helper'; import { makeNestedLabel } from './lib/make_nested_label'; import { METRIC_TYPES } from './metric_agg_types'; +import { GetInternalStartServicesFn } from '../../../types'; + +export interface SerialDiffMetricAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} const serialDiffTitle = i18n.translate('data.search.aggs.metrics.serialDiffTitle', { defaultMessage: 'Serial Diff', @@ -31,11 +36,20 @@ const serialDiffLabel = i18n.translate('data.search.aggs.metrics.serialDiffLabel defaultMessage: 'serial diff', }); -export const serialDiffMetricAgg = new MetricAggType({ - name: METRIC_TYPES.SERIAL_DIFF, - title: serialDiffTitle, - subtype: parentPipelineAggHelper.subtype, - makeLabel: agg => makeNestedLabel(agg, serialDiffLabel), - params: [...parentPipelineAggHelper.params()], - getFormat: parentPipelineAggHelper.getFormat, -}); +export const getSerialDiffMetricAgg = ({ + getInternalStartServices, +}: SerialDiffMetricAggDependencies) => { + return new MetricAggType( + { + name: METRIC_TYPES.SERIAL_DIFF, + title: serialDiffTitle, + subtype: parentPipelineAggHelper.subtype, + makeLabel: agg => makeNestedLabel(agg, serialDiffLabel), + params: [...parentPipelineAggHelper.params()], + getFormat: parentPipelineAggHelper.getFormat, + }, + { + getInternalStartServices, + } + ); +}; diff --git a/src/plugins/data/public/search/aggs/metrics/sibling_pipeline.test.ts b/src/plugins/data/public/search/aggs/metrics/sibling_pipeline.test.ts index 8389ed8262ce54..ab480fe44227ec 100644 --- a/src/plugins/data/public/search/aggs/metrics/sibling_pipeline.test.ts +++ b/src/plugins/data/public/search/aggs/metrics/sibling_pipeline.test.ts @@ -17,27 +17,47 @@ * under the License. */ -import { bucketSumMetricAgg } from './bucket_sum'; -import { bucketAvgMetricAgg } from './bucket_avg'; -import { bucketMinMetricAgg } from './bucket_min'; -import { bucketMaxMetricAgg } from './bucket_max'; +import { getBucketSumMetricAgg } from './bucket_sum'; +import { getBucketAvgMetricAgg } from './bucket_avg'; +import { getBucketMinMetricAgg } from './bucket_min'; +import { getBucketMaxMetricAgg } from './bucket_max'; import { AggConfigs } from '../agg_configs'; import { IMetricAggConfig, MetricAggType } from './metric_agg_type'; -import { mockDataServices, mockAggTypesRegistry } from '../test_helpers'; +import { mockAggTypesRegistry } from '../test_helpers'; +import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; +import { GetInternalStartServicesFn } from '../../../types'; +import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; describe('sibling pipeline aggs', () => { - beforeEach(() => { - mockDataServices(); + const getInternalStartServices: GetInternalStartServicesFn = () => ({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), }); const typesRegistry = mockAggTypesRegistry(); const metrics = [ - { name: 'sum_bucket', title: 'Overall Sum', provider: bucketSumMetricAgg }, - { name: 'avg_bucket', title: 'Overall Average', provider: bucketAvgMetricAgg }, - { name: 'min_bucket', title: 'Overall Min', provider: bucketMinMetricAgg }, - { name: 'max_bucket', title: 'Overall Max', provider: bucketMaxMetricAgg }, + { + name: 'sum_bucket', + title: 'Overall Sum', + provider: getBucketSumMetricAgg({ getInternalStartServices }), + }, + { + name: 'avg_bucket', + title: 'Overall Average', + provider: getBucketAvgMetricAgg({ getInternalStartServices }), + }, + { + name: 'min_bucket', + title: 'Overall Min', + provider: getBucketMinMetricAgg({ getInternalStartServices }), + }, + { + name: 'max_bucket', + title: 'Overall Max', + provider: getBucketMaxMetricAgg({ getInternalStartServices }), + }, ]; metrics.forEach(metric => { diff --git a/src/plugins/data/public/search/aggs/metrics/std_deviation.test.ts b/src/plugins/data/public/search/aggs/metrics/std_deviation.test.ts index 0679831b1e6ac8..6bbff3009cc118 100644 --- a/src/plugins/data/public/search/aggs/metrics/std_deviation.test.ts +++ b/src/plugins/data/public/search/aggs/metrics/std_deviation.test.ts @@ -17,13 +17,25 @@ * under the License. */ -import { IStdDevAggConfig, stdDeviationMetricAgg } from './std_deviation'; +import { + IStdDevAggConfig, + getStdDeviationMetricAgg, + StdDeviationMetricAggDependencies, +} from './std_deviation'; import { AggConfigs } from '../agg_configs'; import { mockAggTypesRegistry } from '../test_helpers'; import { METRIC_TYPES } from './metric_agg_types'; +import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; +import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; describe('AggTypeMetricStandardDeviationProvider class', () => { - const typesRegistry = mockAggTypesRegistry([stdDeviationMetricAgg]); + const aggTypesDependencies: StdDeviationMetricAggDependencies = { + getInternalStartServices: () => ({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + }), + }; + const typesRegistry = mockAggTypesRegistry([getStdDeviationMetricAgg(aggTypesDependencies)]); const getAggConfigs = (customLabel?: string) => { const field = { name: 'memory', @@ -58,7 +70,7 @@ describe('AggTypeMetricStandardDeviationProvider class', () => { it('uses the custom label if it is set', () => { const aggConfigs = getAggConfigs('custom label'); - const responseAggs: any = stdDeviationMetricAgg.getResponseAggs( + const responseAggs: any = getStdDeviationMetricAgg(aggTypesDependencies).getResponseAggs( aggConfigs.aggs[0] as IStdDevAggConfig ); @@ -72,7 +84,7 @@ describe('AggTypeMetricStandardDeviationProvider class', () => { it('uses the default labels if custom label is not set', () => { const aggConfigs = getAggConfigs(); - const responseAggs: any = stdDeviationMetricAgg.getResponseAggs( + const responseAggs: any = getStdDeviationMetricAgg(aggTypesDependencies).getResponseAggs( aggConfigs.aggs[0] as IStdDevAggConfig ); diff --git a/src/plugins/data/public/search/aggs/metrics/std_deviation.ts b/src/plugins/data/public/search/aggs/metrics/std_deviation.ts index 5e069e317e0526..e972132542cebd 100644 --- a/src/plugins/data/public/search/aggs/metrics/std_deviation.ts +++ b/src/plugins/data/public/search/aggs/metrics/std_deviation.ts @@ -23,6 +23,7 @@ import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { getResponseAggConfigClass, IResponseAggConfig } from './lib/get_response_agg_config_class'; import { KBN_FIELD_TYPES } from '../../../../common'; +import { GetInternalStartServicesFn } from '../../../types'; interface ValProp { valProp: string[]; @@ -34,6 +35,10 @@ export interface IStdDevAggConfig extends IResponseAggConfig { valProp: () => ValProp; } +export interface StdDeviationMetricAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} + const responseAggConfigProps = { valProp(this: IStdDevAggConfig) { const customLabel = this.getParam('customLabel'); @@ -75,33 +80,42 @@ const responseAggConfigProps = { }, }; -export const stdDeviationMetricAgg = new MetricAggType({ - name: METRIC_TYPES.STD_DEV, - dslName: 'extended_stats', - title: i18n.translate('data.search.aggs.metrics.standardDeviationTitle', { - defaultMessage: 'Standard Deviation', - }), - makeLabel(agg) { - return i18n.translate('data.search.aggs.metrics.standardDeviationLabel', { - defaultMessage: 'Standard Deviation of {field}', - values: { field: agg.getFieldDisplayName() }, - }); - }, - params: [ +export const getStdDeviationMetricAgg = ({ + getInternalStartServices, +}: StdDeviationMetricAggDependencies) => { + return new MetricAggType( { - name: 'field', - type: 'field', - filterFieldTypes: KBN_FIELD_TYPES.NUMBER, - }, - ], + name: METRIC_TYPES.STD_DEV, + dslName: 'extended_stats', + title: i18n.translate('data.search.aggs.metrics.standardDeviationTitle', { + defaultMessage: 'Standard Deviation', + }), + makeLabel(agg) { + return i18n.translate('data.search.aggs.metrics.standardDeviationLabel', { + defaultMessage: 'Standard Deviation of {field}', + values: { field: agg.getFieldDisplayName() }, + }); + }, + params: [ + { + name: 'field', + type: 'field', + filterFieldTypes: KBN_FIELD_TYPES.NUMBER, + }, + ], - getResponseAggs(agg) { - const ValueAggConfig = getResponseAggConfigClass(agg, responseAggConfigProps); + getResponseAggs(agg) { + const ValueAggConfig = getResponseAggConfigClass(agg, responseAggConfigProps); - return [new ValueAggConfig('std_lower'), new ValueAggConfig('std_upper')]; - }, + return [new ValueAggConfig('std_lower'), new ValueAggConfig('std_upper')]; + }, - getValue(agg, bucket) { - return get(bucket[agg.parentId], agg.valProp()); - }, -}); + getValue(agg, bucket) { + return get(bucket[agg.parentId], agg.valProp()); + }, + }, + { + getInternalStartServices, + } + ); +}; diff --git a/src/plugins/data/public/search/aggs/metrics/sum.ts b/src/plugins/data/public/search/aggs/metrics/sum.ts index ffb117dda08393..545c6d6a4939ea 100644 --- a/src/plugins/data/public/search/aggs/metrics/sum.ts +++ b/src/plugins/data/public/search/aggs/metrics/sum.ts @@ -21,28 +21,40 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; +import { GetInternalStartServicesFn } from '../../../types'; const sumTitle = i18n.translate('data.search.aggs.metrics.sumTitle', { defaultMessage: 'Sum', }); -export const sumMetricAgg = new MetricAggType({ - name: METRIC_TYPES.SUM, - title: sumTitle, - makeLabel(aggConfig) { - return i18n.translate('data.search.aggs.metrics.sumLabel', { - defaultMessage: 'Sum of {field}', - values: { field: aggConfig.getFieldDisplayName() }, - }); - }, - isScalable() { - return true; - }, - params: [ +export interface SumMetricAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} + +export const getSumMetricAgg = ({ getInternalStartServices }: SumMetricAggDependencies) => { + return new MetricAggType( { - name: 'field', - type: 'field', - filterFieldTypes: KBN_FIELD_TYPES.NUMBER, + name: METRIC_TYPES.SUM, + title: sumTitle, + makeLabel(aggConfig) { + return i18n.translate('data.search.aggs.metrics.sumLabel', { + defaultMessage: 'Sum of {field}', + values: { field: aggConfig.getFieldDisplayName() }, + }); + }, + isScalable() { + return true; + }, + params: [ + { + name: 'field', + type: 'field', + filterFieldTypes: KBN_FIELD_TYPES.NUMBER, + }, + ], }, - ], -}); + { + getInternalStartServices, + } + ); +}; diff --git a/src/plugins/data/public/search/aggs/metrics/top_hit.test.ts b/src/plugins/data/public/search/aggs/metrics/top_hit.test.ts index c65a714f262475..8294ad09bae228 100644 --- a/src/plugins/data/public/search/aggs/metrics/top_hit.test.ts +++ b/src/plugins/data/public/search/aggs/metrics/top_hit.test.ts @@ -18,15 +18,23 @@ */ import { dropRight, last } from 'lodash'; -import { topHitMetricAgg } from './top_hit'; +import { getTopHitMetricAgg, TopHitMetricAggDependencies } from './top_hit'; import { AggConfigs } from '../agg_configs'; import { mockAggTypesRegistry } from '../test_helpers'; import { IMetricAggConfig } from './metric_agg_type'; import { KBN_FIELD_TYPES } from '../../../../common'; +import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; +import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; describe('Top hit metric', () => { let aggDsl: Record; let aggConfig: IMetricAggConfig; + const aggTypesDependencies: TopHitMetricAggDependencies = { + getInternalStartServices: () => ({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + }), + }; const init = ({ fieldName = 'field', @@ -36,7 +44,7 @@ describe('Top hit metric', () => { fieldType = KBN_FIELD_TYPES.NUMBER, size = 1, }: any) => { - const typesRegistry = mockAggTypesRegistry([topHitMetricAgg]); + const typesRegistry = mockAggTypesRegistry([getTopHitMetricAgg(aggTypesDependencies)]); const field = { name: fieldName, displayName: fieldName, @@ -91,7 +99,7 @@ describe('Top hit metric', () => { it('should return a label prefixed with Last if sorting in descending order', () => { init({ fieldName: 'bytes' }); - expect(topHitMetricAgg.makeLabel(aggConfig)).toEqual('Last bytes'); + expect(getTopHitMetricAgg(aggTypesDependencies).makeLabel(aggConfig)).toEqual('Last bytes'); }); it('should return a label prefixed with First if sorting in ascending order', () => { @@ -99,7 +107,7 @@ describe('Top hit metric', () => { fieldName: 'bytes', sortOrder: 'asc', }); - expect(topHitMetricAgg.makeLabel(aggConfig)).toEqual('First bytes'); + expect(getTopHitMetricAgg(aggTypesDependencies).makeLabel(aggConfig)).toEqual('First bytes'); }); it('should request the _source field', () => { @@ -140,7 +148,7 @@ describe('Top hit metric', () => { }; init({ fieldName: '@tags' }); - expect(topHitMetricAgg.getValue(aggConfig, bucket)).toBe(null); + expect(getTopHitMetricAgg(aggTypesDependencies).getValue(aggConfig, bucket)).toBe(null); }); // it('should return undefined if the field does not appear in the source', () => { @@ -159,7 +167,7 @@ describe('Top hit metric', () => { }; init({ fieldName: '@tags' }); - expect(topHitMetricAgg.getValue(aggConfig, bucket)).toBe(undefined); + expect(getTopHitMetricAgg(aggTypesDependencies).getValue(aggConfig, bucket)).toBe(undefined); }); it('should return the field value from the top hit', () => { @@ -178,7 +186,7 @@ describe('Top hit metric', () => { }; init({ fieldName: '@tags' }); - expect(topHitMetricAgg.getValue(aggConfig, bucket)).toBe('aaa'); + expect(getTopHitMetricAgg(aggTypesDependencies).getValue(aggConfig, bucket)).toBe('aaa'); }); it('should return the object if the field value is an object', () => { @@ -200,7 +208,9 @@ describe('Top hit metric', () => { init({ fieldName: '@tags' }); - expect(topHitMetricAgg.getValue(aggConfig, bucket)).toEqual({ label: 'aaa' }); + expect(getTopHitMetricAgg(aggTypesDependencies).getValue(aggConfig, bucket)).toEqual({ + label: 'aaa', + }); }); it('should return an array if the field has more than one values', () => { @@ -219,7 +229,10 @@ describe('Top hit metric', () => { }; init({ fieldName: '@tags' }); - expect(topHitMetricAgg.getValue(aggConfig, bucket)).toEqual(['aaa', 'bbb']); + expect(getTopHitMetricAgg(aggTypesDependencies).getValue(aggConfig, bucket)).toEqual([ + 'aaa', + 'bbb', + ]); }); it('should return undefined if the field is not in the source nor in the doc_values field', () => { @@ -241,7 +254,7 @@ describe('Top hit metric', () => { }; init({ fieldName: 'machine.os.raw', readFromDocValues: true }); - expect(topHitMetricAgg.getValue(aggConfig, bucket)).toBe(undefined); + expect(getTopHitMetricAgg(aggTypesDependencies).getValue(aggConfig, bucket)).toBe(undefined); }); describe('Multivalued field and first/last X docs', () => { @@ -250,7 +263,9 @@ describe('Top hit metric', () => { fieldName: 'bytes', size: 2, }); - expect(topHitMetricAgg.makeLabel(aggConfig)).toEqual('Last 2 bytes'); + expect(getTopHitMetricAgg(aggTypesDependencies).makeLabel(aggConfig)).toEqual( + 'Last 2 bytes' + ); }); it('should return a label prefixed with First X docs if sorting in ascending order', () => { @@ -259,7 +274,9 @@ describe('Top hit metric', () => { size: 2, sortOrder: 'asc', }); - expect(topHitMetricAgg.makeLabel(aggConfig)).toEqual('First 2 bytes'); + expect(getTopHitMetricAgg(aggTypesDependencies).makeLabel(aggConfig)).toEqual( + 'First 2 bytes' + ); }); [ @@ -334,7 +351,9 @@ describe('Top hit metric', () => { }; init({ fieldName: 'bytes', aggregate: agg.type }); - expect(topHitMetricAgg.getValue(aggConfig, bucket)).toEqual(agg.result); + expect(getTopHitMetricAgg(aggTypesDependencies).getValue(aggConfig, bucket)).toEqual( + agg.result + ); }); it(`should return the result of the ${agg.type} aggregation over the last X docs - ${agg.description}`, () => { @@ -358,7 +377,9 @@ describe('Top hit metric', () => { }; init({ fieldName: 'bytes', aggregate: agg.type }); - expect(topHitMetricAgg.getValue(aggConfig, bucket)).toEqual(agg.result); + expect(getTopHitMetricAgg(aggTypesDependencies).getValue(aggConfig, bucket)).toEqual( + agg.result + ); }); }); }); diff --git a/src/plugins/data/public/search/aggs/metrics/top_hit.ts b/src/plugins/data/public/search/aggs/metrics/top_hit.ts index d0c668c577e627..15da2b485aee76 100644 --- a/src/plugins/data/public/search/aggs/metrics/top_hit.ts +++ b/src/plugins/data/public/search/aggs/metrics/top_hit.ts @@ -22,6 +22,11 @@ import { i18n } from '@kbn/i18n'; import { IMetricAggConfig, MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { KBN_FIELD_TYPES } from '../../../../common'; +import { GetInternalStartServicesFn } from '../../../types'; + +export interface TopHitMetricAggDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} const isNumericFieldSelected = (agg: IMetricAggConfig) => { const field = agg.getParam('field'); @@ -29,214 +34,225 @@ const isNumericFieldSelected = (agg: IMetricAggConfig) => { return field && field.type && field.type === KBN_FIELD_TYPES.NUMBER; }; -export const topHitMetricAgg = new MetricAggType({ - name: METRIC_TYPES.TOP_HITS, - title: i18n.translate('data.search.aggs.metrics.topHitTitle', { - defaultMessage: 'Top Hit', - }), - makeLabel(aggConfig) { - const lastPrefixLabel = i18n.translate('data.search.aggs.metrics.topHit.lastPrefixLabel', { - defaultMessage: 'Last', - }); - const firstPrefixLabel = i18n.translate('data.search.aggs.metrics.topHit.firstPrefixLabel', { - defaultMessage: 'First', - }); - - let prefix = - aggConfig.getParam('sortOrder').value === 'desc' ? lastPrefixLabel : firstPrefixLabel; - - const size = aggConfig.getParam('size'); - - if (size !== 1) { - prefix += ` ${size}`; - } - - const field = aggConfig.getParam('field'); - - return `${prefix} ${field ? field.displayName : ''}`; - }, - params: [ +export const getTopHitMetricAgg = ({ getInternalStartServices }: TopHitMetricAggDependencies) => { + return new MetricAggType( { - name: 'field', - type: 'field', - onlyAggregatable: false, - filterFieldTypes: Object.values(KBN_FIELD_TYPES).filter( - type => type !== KBN_FIELD_TYPES.HISTOGRAM - ), - write(agg, output) { - const field = agg.getParam('field'); - output.params = {}; - - if (field.scripted) { - output.params.script_fields = { - [field.name]: { - script: { - source: field.script, - lang: field.lang, - }, - }, - }; - } else { - if (field.readFromDocValues) { - // always format date fields as date_time to avoid - // displaying unformatted dates like epoch_millis - // or other not-accepted momentjs formats - const format = field.type === KBN_FIELD_TYPES.DATE ? 'date_time' : 'use_field_mapping'; - output.params.docvalue_fields = [{ field: field.name, format }]; + name: METRIC_TYPES.TOP_HITS, + title: i18n.translate('data.search.aggs.metrics.topHitTitle', { + defaultMessage: 'Top Hit', + }), + makeLabel(aggConfig) { + const lastPrefixLabel = i18n.translate('data.search.aggs.metrics.topHit.lastPrefixLabel', { + defaultMessage: 'Last', + }); + const firstPrefixLabel = i18n.translate( + 'data.search.aggs.metrics.topHit.firstPrefixLabel', + { + defaultMessage: 'First', } - output.params._source = field.name === '_source' ? true : field.name; + ); + + let prefix = + aggConfig.getParam('sortOrder').value === 'desc' ? lastPrefixLabel : firstPrefixLabel; + + const size = aggConfig.getParam('size'); + + if (size !== 1) { + prefix += ` ${size}`; } + + const field = aggConfig.getParam('field'); + + return `${prefix} ${field ? field.displayName : ''}`; }, - }, - { - name: 'aggregate', - type: 'optioned', - options: [ + params: [ { - text: i18n.translate('data.search.aggs.metrics.topHit.minLabel', { - defaultMessage: 'Min', - }), - isCompatible: isNumericFieldSelected, - disabled: true, - value: 'min', - }, - { - text: i18n.translate('data.search.aggs.metrics.topHit.maxLabel', { - defaultMessage: 'Max', - }), - isCompatible: isNumericFieldSelected, - disabled: true, - value: 'max', + name: 'field', + type: 'field', + onlyAggregatable: false, + filterFieldTypes: Object.values(KBN_FIELD_TYPES).filter( + type => type !== KBN_FIELD_TYPES.HISTOGRAM + ), + write(agg, output) { + const field = agg.getParam('field'); + output.params = {}; + + if (field.scripted) { + output.params.script_fields = { + [field.name]: { + script: { + source: field.script, + lang: field.lang, + }, + }, + }; + } else { + if (field.readFromDocValues) { + // always format date fields as date_time to avoid + // displaying unformatted dates like epoch_millis + // or other not-accepted momentjs formats + const format = + field.type === KBN_FIELD_TYPES.DATE ? 'date_time' : 'use_field_mapping'; + output.params.docvalue_fields = [{ field: field.name, format }]; + } + output.params._source = field.name === '_source' ? true : field.name; + } + }, }, { - text: i18n.translate('data.search.aggs.metrics.topHit.sumLabel', { - defaultMessage: 'Sum', - }), - isCompatible: isNumericFieldSelected, - disabled: true, - value: 'sum', + name: 'aggregate', + type: 'optioned', + options: [ + { + text: i18n.translate('data.search.aggs.metrics.topHit.minLabel', { + defaultMessage: 'Min', + }), + isCompatible: isNumericFieldSelected, + disabled: true, + value: 'min', + }, + { + text: i18n.translate('data.search.aggs.metrics.topHit.maxLabel', { + defaultMessage: 'Max', + }), + isCompatible: isNumericFieldSelected, + disabled: true, + value: 'max', + }, + { + text: i18n.translate('data.search.aggs.metrics.topHit.sumLabel', { + defaultMessage: 'Sum', + }), + isCompatible: isNumericFieldSelected, + disabled: true, + value: 'sum', + }, + { + text: i18n.translate('data.search.aggs.metrics.topHit.averageLabel', { + defaultMessage: 'Average', + }), + isCompatible: isNumericFieldSelected, + disabled: true, + value: 'average', + }, + { + text: i18n.translate('data.search.aggs.metrics.topHit.concatenateLabel', { + defaultMessage: 'Concatenate', + }), + isCompatible(aggConfig: IMetricAggConfig) { + return _.get(aggConfig.params, 'field.filterFieldTypes', '*') === '*'; + }, + disabled: true, + value: 'concat', + }, + ], + write: _.noop, }, { - text: i18n.translate('data.search.aggs.metrics.topHit.averageLabel', { - defaultMessage: 'Average', - }), - isCompatible: isNumericFieldSelected, - disabled: true, - value: 'average', + name: 'size', + default: 1, }, { - text: i18n.translate('data.search.aggs.metrics.topHit.concatenateLabel', { - defaultMessage: 'Concatenate', - }), - isCompatible(aggConfig: IMetricAggConfig) { - return _.get(aggConfig.params, 'field.filterFieldTypes', '*') === '*'; + name: 'sortField', + type: 'field', + filterFieldTypes: [ + KBN_FIELD_TYPES.NUMBER, + KBN_FIELD_TYPES.DATE, + KBN_FIELD_TYPES.IP, + KBN_FIELD_TYPES.STRING, + ], + default(agg: IMetricAggConfig) { + return agg.getIndexPattern().timeFieldName; }, - disabled: true, - value: 'concat', - }, - ], - write: _.noop, - }, - { - name: 'size', - default: 1, - }, - { - name: 'sortField', - type: 'field', - filterFieldTypes: [ - KBN_FIELD_TYPES.NUMBER, - KBN_FIELD_TYPES.DATE, - KBN_FIELD_TYPES.IP, - KBN_FIELD_TYPES.STRING, - ], - default(agg: IMetricAggConfig) { - return agg.getIndexPattern().timeFieldName; - }, - write: _.noop, // prevent default write, it is handled below - }, - { - name: 'sortOrder', - type: 'optioned', - default: 'desc', - options: [ - { - text: i18n.translate('data.search.aggs.metrics.topHit.descendingLabel', { - defaultMessage: 'Descending', - }), - value: 'desc', + write: _.noop, // prevent default write, it is handled below }, { - text: i18n.translate('data.search.aggs.metrics.topHit.ascendingLabel', { - defaultMessage: 'Ascending', - }), - value: 'asc', - }, - ], - write(agg, output) { - const sortField = agg.params.sortField; - const sortOrder = agg.params.sortOrder; - - if (sortField.scripted) { - output.params.sort = [ + name: 'sortOrder', + type: 'optioned', + default: 'desc', + options: [ { - _script: { - script: { - source: sortField.script, - lang: sortField.lang, - }, - type: sortField.type, - order: sortOrder.value, - }, + text: i18n.translate('data.search.aggs.metrics.topHit.descendingLabel', { + defaultMessage: 'Descending', + }), + value: 'desc', }, - ]; - } else { - output.params.sort = [ { - [sortField.name]: { - order: sortOrder.value, - }, + text: i18n.translate('data.search.aggs.metrics.topHit.ascendingLabel', { + defaultMessage: 'Ascending', + }), + value: 'asc', }, - ]; + ], + write(agg, output) { + const sortField = agg.params.sortField; + const sortOrder = agg.params.sortOrder; + + if (sortField.scripted) { + output.params.sort = [ + { + _script: { + script: { + source: sortField.script, + lang: sortField.lang, + }, + type: sortField.type, + order: sortOrder.value, + }, + }, + ]; + } else { + output.params.sort = [ + { + [sortField.name]: { + order: sortOrder.value, + }, + }, + ]; + } + }, + }, + ], + getValue(agg, bucket) { + const hits: any[] = _.get(bucket, `${agg.id}.hits.hits`); + if (!hits || !hits.length) { + return null; } - }, - }, - ], - getValue(agg, bucket) { - const hits: any[] = _.get(bucket, `${agg.id}.hits.hits`); - if (!hits || !hits.length) { - return null; - } - const path = agg.getParam('field').name; + const path = agg.getParam('field').name; - let values = _.flatten( - hits.map(hit => - path === '_source' ? hit._source : agg.getIndexPattern().flattenHit(hit, true)[path] - ) - ); + let values = _.flatten( + hits.map(hit => + path === '_source' ? hit._source : agg.getIndexPattern().flattenHit(hit, true)[path] + ) + ); - if (values.length === 1) { - values = values[0]; - } + if (values.length === 1) { + values = values[0]; + } + + if (Array.isArray(values)) { + if (!_.compact(values).length) { + return null; + } + + const aggregate = agg.getParam('aggregate'); - if (Array.isArray(values)) { - if (!_.compact(values).length) { - return null; - } - - const aggregate = agg.getParam('aggregate'); - - switch (aggregate.value) { - case 'max': - return _.max(values); - case 'min': - return _.min(values); - case 'sum': - return _.sum(values); - case 'average': - return _.sum(values) / values.length; - } + switch (aggregate.value) { + case 'max': + return _.max(values); + case 'min': + return _.min(values); + case 'sum': + return _.sum(values); + case 'average': + return _.sum(values) / values.length; + } + } + return values; + }, + }, + { + getInternalStartServices, } - return values; - }, -}); + ); +}; diff --git a/src/plugins/data/public/search/aggs/param_types/field.test.ts b/src/plugins/data/public/search/aggs/param_types/field.test.ts index 0182471392910c..ea7931130b84a0 100644 --- a/src/plugins/data/public/search/aggs/param_types/field.test.ts +++ b/src/plugins/data/public/search/aggs/param_types/field.test.ts @@ -18,11 +18,20 @@ */ import { BaseParamType } from './base'; -import { FieldParamType } from './field'; +import { FieldParamType, FieldParamTypeDependencies } from './field'; import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '../../../../common'; import { IAggConfig } from '../agg_config'; +import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; +import { notificationServiceMock } from '../../../../../../../src/core/public/mocks'; describe('Field', () => { + const fieldParamTypeDependencies: FieldParamTypeDependencies = { + getInternalStartServices: () => ({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + }), + }; + const indexPattern = { id: '1234', title: 'logstash-*', @@ -52,10 +61,13 @@ describe('Field', () => { describe('constructor', () => { it('it is an instance of BaseParamType', () => { - const aggParam = new FieldParamType({ - name: 'field', - type: 'field', - }); + const aggParam = new FieldParamType( + { + name: 'field', + type: 'field', + }, + fieldParamTypeDependencies + ); expect(aggParam instanceof BaseParamType).toBeTruthy(); }); @@ -63,10 +75,13 @@ describe('Field', () => { describe('getAvailableFields', () => { it('should return only aggregatable fields by default', () => { - const aggParam = new FieldParamType({ - name: 'field', - type: 'field', - }); + const aggParam = new FieldParamType( + { + name: 'field', + type: 'field', + }, + fieldParamTypeDependencies + ); const fields = aggParam.getAvailableFields(agg); @@ -78,10 +93,13 @@ describe('Field', () => { }); it('should return all fields if onlyAggregatable is false', () => { - const aggParam = new FieldParamType({ - name: 'field', - type: 'field', - }); + const aggParam = new FieldParamType( + { + name: 'field', + type: 'field', + }, + fieldParamTypeDependencies + ); aggParam.onlyAggregatable = false; @@ -91,10 +109,13 @@ describe('Field', () => { }); it('should return all fields if filterFieldTypes was not specified', () => { - const aggParam = new FieldParamType({ - name: 'field', - type: 'field', - }); + const aggParam = new FieldParamType( + { + name: 'field', + type: 'field', + }, + fieldParamTypeDependencies + ); indexPattern.fields[1].aggregatable = true; diff --git a/src/plugins/data/public/search/aggs/param_types/field.ts b/src/plugins/data/public/search/aggs/param_types/field.ts index 34b77e14a3a71c..4d67f41905c5a4 100644 --- a/src/plugins/data/public/search/aggs/param_types/field.ts +++ b/src/plugins/data/public/search/aggs/param_types/field.ts @@ -24,7 +24,7 @@ import { BaseParamType } from './base'; import { propFilter } from '../filter'; import { isNestedField, KBN_FIELD_TYPES } from '../../../../common'; import { Field as IndexPatternField } from '../../../index_patterns'; -import { getNotifications } from '../../../../public/services'; +import { GetInternalStartServicesFn } from '../../../types'; const filterByType = propFilter('type'); @@ -32,13 +32,20 @@ export type FieldTypes = KBN_FIELD_TYPES | KBN_FIELD_TYPES[] | '*'; // TODO need to make a more explicit interface for this export type IFieldParamType = FieldParamType; +export interface FieldParamTypeDependencies { + getInternalStartServices: GetInternalStartServicesFn; +} + export class FieldParamType extends BaseParamType { required = true; scriptable = true; filterFieldTypes: FieldTypes; onlyAggregatable: boolean; - constructor(config: Record) { + constructor( + config: Record, + { getInternalStartServices }: FieldParamTypeDependencies + ) { super(config); this.filterFieldTypes = config.filterFieldTypes || '*'; @@ -87,7 +94,7 @@ export class FieldParamType extends BaseParamType { // @ts-ignore const validField = this.getAvailableFields(aggConfig).find((f: any) => f.name === fieldName); if (!validField) { - getNotifications().toasts.addDanger( + getInternalStartServices().notifications.toasts.addDanger( i18n.translate( 'data.search.aggs.paramTypes.field.invalidSavedFieldParameterErrorMessage', { diff --git a/src/plugins/data/public/search/aggs/test_helpers/mock_agg_types_registry.ts b/src/plugins/data/public/search/aggs/test_helpers/mock_agg_types_registry.ts index 57d27b7da63133..2383affa2a8c50 100644 --- a/src/plugins/data/public/search/aggs/test_helpers/mock_agg_types_registry.ts +++ b/src/plugins/data/public/search/aggs/test_helpers/mock_agg_types_registry.ts @@ -18,12 +18,13 @@ */ // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { coreMock } from '../../../../../../../src/core/public/mocks'; +import { coreMock, notificationServiceMock } from '../../../../../../../src/core/public/mocks'; import { AggTypesRegistry, AggTypesRegistryStart } from '../agg_types_registry'; import { getAggTypes } from '../agg_types'; -import { BucketAggType } from '../buckets/_bucket_agg_type'; +import { BucketAggType } from '../buckets/bucket_agg_type'; import { MetricAggType } from '../metrics/metric_agg_type'; import { queryServiceMock } from '../../../query/mocks'; +import { fieldFormatsServiceMock } from '../../../field_formats/mocks'; /** * Testing utility which creates a new instance of AggTypesRegistry, @@ -55,8 +56,11 @@ export function mockAggTypesRegistry | MetricAggTyp const core = coreMock.createSetup(); const aggTypes = getAggTypes({ uiSettings: core.uiSettings, - notifications: core.notifications, query: queryServiceMock.createSetupContract(), + getInternalStartServices: () => ({ + fieldFormats: fieldFormatsServiceMock.createStartContract(), + notifications: notificationServiceMock.createStartContract(), + }), }); aggTypes.buckets.forEach(type => registrySetup.registerBucket(type)); diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index dc1c99f76d59a6..42f31ef450d28c 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -26,6 +26,7 @@ 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 { QuerySetup } from '../query/query_service'; +import { GetInternalStartServicesFn } from '../types'; import { SearchInterceptor } from './search_interceptor'; import { getAggTypes, @@ -44,6 +45,7 @@ import { interface SearchServiceSetupDependencies { packageInfo: PackageInfo; query: QuerySetup; + getInternalStartServices: GetInternalStartServicesFn; } /** @@ -81,7 +83,7 @@ export class SearchService implements Plugin { public setup( core: CoreSetup, - { packageInfo, query }: SearchServiceSetupDependencies + { packageInfo, query, getInternalStartServices }: SearchServiceSetupDependencies ): ISearchSetup { this.esClient = getEsClient(core.injectedMetadata, core.http, packageInfo); this.registerSearchStrategyProvider(SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider); @@ -91,7 +93,7 @@ export class SearchService implements Plugin { const aggTypes = getAggTypes({ query, uiSettings: core.uiSettings, - notifications: core.notifications, + getInternalStartServices, }); aggTypes.buckets.forEach(b => aggTypesSetup.registerBucket(b)); diff --git a/src/plugins/data/public/types.ts b/src/plugins/data/public/types.ts index 45160cbf301799..e24e01d2412781 100644 --- a/src/plugins/data/public/types.ts +++ b/src/plugins/data/public/types.ts @@ -71,3 +71,12 @@ export interface IDataPluginServices extends Partial { storage: IStorageWrapper; data: DataPublicPluginStart; } + +/** @internal **/ +export interface InternalStartServices { + fieldFormats: FieldFormatsStart; + notifications: CoreStart['notifications']; +} + +/** @internal **/ +export type GetInternalStartServicesFn = () => InternalStartServices; diff --git a/src/plugins/data/server/index_patterns/routes.ts b/src/plugins/data/server/index_patterns/routes.ts index 8f017a73083ecf..8b9fa28c771654 100644 --- a/src/plugins/data/server/index_patterns/routes.ts +++ b/src/plugins/data/server/index_patterns/routes.ts @@ -70,7 +70,22 @@ export function registerRoutes(http: HttpServiceSetup) { }, }); } catch (error) { - return response.notFound(); + if ( + typeof error === 'object' && + !!error?.isBoom && + !!error?.output?.payload && + typeof error?.output?.payload === 'object' + ) { + const payload = error?.output?.payload; + return response.notFound({ + body: { + message: payload.message, + attributes: payload, + }, + }); + } else { + return response.notFound(); + } } } ); diff --git a/src/plugins/data/server/saved_objects/search_migrations.test.ts b/src/plugins/data/server/saved_objects/search_migrations.test.ts index 7fdf2e14aefedf..f9b4af7d6d2bf6 100644 --- a/src/plugins/data/server/saved_objects/search_migrations.test.ts +++ b/src/plugins/data/server/saved_objects/search_migrations.test.ts @@ -23,6 +23,38 @@ import { searchSavedObjectTypeMigrations } from './search_migrations'; const savedObjectMigrationContext = (null as unknown) as SavedObjectMigrationContext; describe('migration search', () => { + describe('6.7.2', () => { + const migrationFn = searchSavedObjectTypeMigrations['6.7.2']; + + it('should migrate obsolete match_all query', () => { + const migratedDoc = migrationFn( + { + type: 'search', + attributes: { + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ + query: { + match_all: {}, + }, + }), + }, + }, + }, + savedObjectMigrationContext + ); + const migratedSearchSource = JSON.parse( + migratedDoc.attributes.kibanaSavedObjectMeta.searchSourceJSON + ); + + expect(migratedSearchSource).toEqual({ + query: { + query: '', + language: 'kuery', + }, + }); + }); + }); + describe('7.0.0', () => { const migrationFn = searchSavedObjectTypeMigrations['7.0.0']; diff --git a/src/plugins/data/server/saved_objects/search_migrations.ts b/src/plugins/data/server/saved_objects/search_migrations.ts index db545e52ce170e..45fa5e11e2a3d7 100644 --- a/src/plugins/data/server/saved_objects/search_migrations.ts +++ b/src/plugins/data/server/saved_objects/search_migrations.ts @@ -19,6 +19,41 @@ import { flow, get } from 'lodash'; import { SavedObjectMigrationFn } from 'kibana/server'; +import { DEFAULT_QUERY_LANGUAGE } from '../../common'; + +const migrateMatchAllQuery: SavedObjectMigrationFn = doc => { + const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); + + if (searchSourceJSON) { + let searchSource: any; + + try { + searchSource = JSON.parse(searchSourceJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + + if (searchSource.query?.match_all) { + return { + ...doc, + attributes: { + ...doc.attributes, + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ + ...searchSource, + query: { + query: '', + language: DEFAULT_QUERY_LANGUAGE, + }, + }), + }, + }, + }; + } + } + + return doc; +}; const migrateIndexPattern: SavedObjectMigrationFn = doc => { const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); @@ -87,6 +122,7 @@ const migrateSearchSortToNestedArray: SavedObjectMigrationFn = doc => { }; export const searchSavedObjectTypeMigrations = { + '6.7.2': flow(migrateMatchAllQuery), '7.0.0': flow(setNewReferences), '7.4.0': flow(migrateSearchSortToNestedArray), }; diff --git a/src/plugins/discover/public/services.ts b/src/plugins/discover/public/services.ts index 3a28759d82b71d..37e2144800ea11 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/containers/embeddable_child_panel.test.tsx b/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.test.tsx index 9e47da5cea0329..2a0ffd723850b3 100644 --- a/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.test.tsx +++ b/src/plugins/embeddable/public/lib/containers/embeddable_child_panel.test.tsx @@ -29,7 +29,7 @@ import { ContactCardEmbeddable, } from '../test_samples/embeddables/contact_card/contact_card_embeddable'; // eslint-disable-next-line -import { inspectorPluginMock } from 'src/plugins/inspector/public/mocks'; +import { inspectorPluginMock } from '../../../../inspector/public/mocks'; import { mount } from 'enzyme'; import { embeddablePluginMock } from '../../mocks'; 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 56facc37fc6661..ddd84b05443455 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 816001ba42ff1d..715827a72c61b9 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/embeddable/public/lib/embeddables/with_subscription.tsx b/src/plugins/embeddable/public/lib/embeddables/with_subscription.tsx index 47b8001961cf51..9bc5889715c760 100644 --- a/src/plugins/embeddable/public/lib/embeddables/with_subscription.tsx +++ b/src/plugins/embeddable/public/lib/embeddables/with_subscription.tsx @@ -23,18 +23,19 @@ import { IEmbeddable, EmbeddableInput, EmbeddableOutput } from './i_embeddable'; export const withEmbeddableSubscription = < I extends EmbeddableInput, O extends EmbeddableOutput, - E extends IEmbeddable = IEmbeddable + E extends IEmbeddable = IEmbeddable, + ExtraProps = {} >( - WrappedComponent: React.ComponentType<{ input: I; output: O; embeddable: E }> -): React.ComponentType<{ embeddable: E }> => + WrappedComponent: React.ComponentType<{ input: I; output: O; embeddable: E } & ExtraProps> +): React.ComponentType<{ embeddable: E } & ExtraProps> => class WithEmbeddableSubscription extends React.Component< - { embeddable: E }, + { embeddable: E } & ExtraProps, { input: I; output: O } > { private subscription?: Rx.Subscription; private mounted: boolean = false; - constructor(props: { embeddable: E }) { + constructor(props: { embeddable: E } & ExtraProps) { super(props); this.state = { input: this.props.embeddable.getInput(), @@ -71,6 +72,7 @@ export const withEmbeddableSubscription = < input={this.state.input} output={this.state.output} embeddable={this.props.embeddable} + {...this.props} /> ); } diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx index 649677dc67c7d1..1e7cbb2f3dafcf 100644 --- a/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx +++ b/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx @@ -25,7 +25,7 @@ import { nextTick } from 'test_utils/enzyme_helpers'; import { findTestSubject } from '@elastic/eui/lib/test'; import { I18nProvider } from '@kbn/i18n/react'; import { CONTEXT_MENU_TRIGGER } from '../triggers'; -import { Action, UiActionsStart, ActionType } from 'src/plugins/ui_actions/public'; +import { Action, UiActionsStart, ActionType } from '../../../../ui_actions/public'; import { Trigger, ViewMode } from '../types'; import { isErrorEmbeddable } from '../embeddables'; import { EmbeddablePanel } from './embeddable_panel'; @@ -41,7 +41,7 @@ import { ContactCardEmbeddableOutput, } from '../test_samples/embeddables/contact_card/contact_card_embeddable'; // eslint-disable-next-line -import { inspectorPluginMock } from 'src/plugins/inspector/public/mocks'; +import { inspectorPluginMock } from '../../../../inspector/public/mocks'; import { EuiBadge } from '@elastic/eui'; import { embeddablePluginMock } from '../../mocks'; diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.test.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.test.tsx index ee31127cb5a405..491eaad9faefa4 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.test.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.test.tsx @@ -27,7 +27,7 @@ import { ContactCardEmbeddable, } from '../../../test_samples'; // eslint-disable-next-line -import { inspectorPluginMock } from 'src/plugins/inspector/public/mocks'; +import { inspectorPluginMock } from '../../../../../../../plugins/inspector/public/mocks'; import { EmbeddableOutput, isErrorEmbeddable, ErrorEmbeddable } from '../../../embeddables'; import { of } from '../../../../tests/helpers'; import { esFilters } from '../../../../../../../plugins/data/public'; diff --git a/src/plugins/embeddable/public/mocks.ts b/src/plugins/embeddable/public/mocks.ts index 2ee05d8316aced..65b15f3a7614fb 100644 --- a/src/plugins/embeddable/public/mocks.ts +++ b/src/plugins/embeddable/public/mocks.ts @@ -16,11 +16,12 @@ * specific language governing permissions and limitations * under the License. */ - import { EmbeddableStart, EmbeddableSetup } from '.'; import { EmbeddablePublicPlugin } from './plugin'; import { coreMock } from '../../../core/public/mocks'; +// eslint-disable-next-line +import { inspectorPluginMock } from '../../inspector/public/mocks'; // eslint-disable-next-line import { uiActionsPluginMock } from '../../ui_actions/public/mocks'; @@ -39,6 +40,7 @@ const createStartContract = (): Start => { const startContract: Start = { getEmbeddableFactories: jest.fn(), getEmbeddableFactory: jest.fn(), + EmbeddablePanel: jest.fn(), }; return startContract; }; @@ -48,7 +50,11 @@ const createInstance = () => { const setup = plugin.setup(coreMock.createSetup(), { uiActions: uiActionsPluginMock.createSetupContract(), }); - const doStart = () => plugin.start(coreMock.createStart()); + const doStart = () => + plugin.start(coreMock.createStart(), { + uiActions: uiActionsPluginMock.createStartContract(), + inspector: inspectorPluginMock.createStartContract(), + }); return { plugin, setup, diff --git a/src/plugins/embeddable/public/plugin.ts b/src/plugins/embeddable/public/plugin.tsx similarity index 76% rename from src/plugins/embeddable/public/plugin.ts rename to src/plugins/embeddable/public/plugin.tsx index a483f90f76dde7..01fbf52c801823 100644 --- a/src/plugins/embeddable/public/plugin.ts +++ b/src/plugins/embeddable/public/plugin.tsx @@ -16,7 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -import { UiActionsSetup } from 'src/plugins/ui_actions/public'; +import React from 'react'; +import { getSavedObjectFinder } from '../../saved_objects/public'; +import { UiActionsSetup, UiActionsStart } from '../../ui_actions/public'; +import { Start as InspectorStart } from '../../inspector/public'; import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../core/public'; import { EmbeddableFactoryRegistry, EmbeddableFactoryProvider } from './types'; import { bootstrap } from './bootstrap'; @@ -26,6 +29,7 @@ import { EmbeddableOutput, defaultEmbeddableFactoryProvider, IEmbeddable, + EmbeddablePanel, } from './lib'; import { EmbeddableFactoryDefinition } from './lib/embeddables/embeddable_factory_definition'; @@ -33,6 +37,11 @@ export interface EmbeddableSetupDependencies { uiActions: UiActionsSetup; } +export interface EmbeddableStartDependencies { + uiActions: UiActionsStart; + inspector: InspectorStart; +} + export interface EmbeddableSetup { registerEmbeddableFactory: ( id: string, @@ -50,6 +59,7 @@ export interface EmbeddableStart { embeddableFactoryId: string ) => EmbeddableFactory | undefined; getEmbeddableFactories: () => IterableIterator; + EmbeddablePanel: React.FC<{ embeddable: IEmbeddable; hideHeader?: boolean }>; } export class EmbeddablePublicPlugin implements Plugin { @@ -78,7 +88,10 @@ export class EmbeddablePublicPlugin implements Plugin { this.embeddableFactories.set( def.type, @@ -89,15 +102,36 @@ export class EmbeddablePublicPlugin implements Plugin { - this.ensureFactoriesExist(); - return this.embeddableFactories.values(); - }, + getEmbeddableFactories: this.getEmbeddableFactories, + EmbeddablePanel: ({ + embeddable, + hideHeader, + }: { + embeddable: IEmbeddable; + hideHeader?: boolean; + }) => ( + + ), }; } public stop() {} + private getEmbeddableFactories = () => { + this.ensureFactoriesExist(); + return this.embeddableFactories.values(); + }; + private registerEmbeddableFactory = ( embeddableFactoryId: string, factory: EmbeddableFactoryDefinition @@ -130,11 +164,11 @@ export class EmbeddablePublicPlugin implements Plugin { this.embeddableFactoryDefinitions.forEach(def => this.ensureFactoryExists(def.type)); - } + }; - private ensureFactoryExists(type: string) { + private ensureFactoryExists = (type: string) => { if (!this.embeddableFactories.get(type)) { const def = this.embeddableFactoryDefinitions.get(type); if (!def) return; @@ -145,5 +179,5 @@ export class EmbeddablePublicPlugin implements Plugin { diff --git a/src/plugins/embeddable/public/tests/test_plugin.ts b/src/plugins/embeddable/public/tests/test_plugin.ts index e199ef193aa1cc..e13a906e30338f 100644 --- a/src/plugins/embeddable/public/tests/test_plugin.ts +++ b/src/plugins/embeddable/public/tests/test_plugin.ts @@ -18,9 +18,11 @@ */ import { CoreSetup, CoreStart } from 'src/core/public'; +import { UiActionsStart } from '../../../ui_actions/public'; // eslint-disable-next-line -import { uiActionsPluginMock } from 'src/plugins/ui_actions/public/mocks'; -import { UiActionsStart } from 'src/plugins/ui_actions/public'; +import { uiActionsPluginMock } from '../../../ui_actions/public/mocks'; +// eslint-disable-next-line +import { inspectorPluginMock } from '../../../inspector/public/mocks'; import { coreMock } from '../../../../core/public/mocks'; import { EmbeddablePublicPlugin, EmbeddableSetup, EmbeddableStart } from '../plugin'; @@ -48,7 +50,10 @@ export const testPlugin = ( coreStart, setup, doStart: (anotherCoreStart: CoreStart = coreStart) => { - const start = plugin.start(anotherCoreStart); + const start = plugin.start(anotherCoreStart, { + uiActions: uiActionsPluginMock.createStartContract(), + inspector: inspectorPluginMock.createStartContract(), + }); return start; }, uiActions: uiActions.doStart(coreStart), diff --git a/src/plugins/es_ui_shared/kibana.json b/src/plugins/es_ui_shared/kibana.json new file mode 100644 index 00000000000000..5a3db3b3440901 --- /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 944b800c66a284..6db6248f4c68fa 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/home/public/application/components/__snapshots__/synopsis.test.js.snap b/src/plugins/home/public/application/components/__snapshots__/synopsis.test.js.snap index 594d67d9c8eb01..d757d6a8b73051 100644 --- a/src/plugins/home/public/application/components/__snapshots__/synopsis.test.js.snap +++ b/src/plugins/home/public/application/components/__snapshots__/synopsis.test.js.snap @@ -9,8 +9,8 @@ exports[`props iconType 1`] = ` href="link_to_item" icon={ } diff --git a/src/plugins/home/public/application/components/synopsis.js b/src/plugins/home/public/application/components/synopsis.js index f43c377b4e5b90..b6fa85db2bfe90 100644 --- a/src/plugins/home/public/application/components/synopsis.js +++ b/src/plugins/home/public/application/components/synopsis.js @@ -35,9 +35,9 @@ export function Synopsis({ }) { let optionalImg; if (iconUrl) { - optionalImg = ; + optionalImg = ; } else if (iconType) { - optionalImg = ; + optionalImg = ; } const classes = classNames('homSynopsis__card', { diff --git a/src/plugins/home/public/application/components/tutorial/__snapshots__/introduction.test.js.snap b/src/plugins/home/public/application/components/tutorial/__snapshots__/introduction.test.js.snap index b35545787e4a41..410d29a42cac96 100644 --- a/src/plugins/home/public/application/components/tutorial/__snapshots__/introduction.test.js.snap +++ b/src/plugins/home/public/application/components/tutorial/__snapshots__/introduction.test.js.snap @@ -15,7 +15,6 @@ exports[`props exportedFieldsUrl 1`] = ` >

Great tutorial -  

@@ -56,6 +55,7 @@ exports[`props iconType 1`] = ` > @@ -67,7 +67,6 @@ exports[`props iconType 1`] = ` >

Great tutorial -  

@@ -97,7 +96,7 @@ exports[`props isBeta 1`] = ` >

Great tutorial -   +   @@ -130,7 +129,6 @@ exports[`props previewUrl 1`] = ` >

Great tutorial -  

@@ -169,7 +167,6 @@ exports[`render 1`] = ` >

Great tutorial -  

diff --git a/src/plugins/home/public/application/components/tutorial/introduction.js b/src/plugins/home/public/application/components/tutorial/introduction.js index bc5f30622f1a51..c36d150c42d3db 100644 --- a/src/plugins/home/public/application/components/tutorial/introduction.js +++ b/src/plugins/home/public/application/components/tutorial/introduction.js @@ -76,7 +76,7 @@ function IntroductionUI({ if (iconType) { icon = ( - + ); } @@ -99,8 +99,13 @@ function IntroductionUI({

- {title}   - {betaBadge} + {title} + {betaBadge && ( + <> +   + {betaBadge} + + )}

diff --git a/src/plugins/index_pattern_management/kibana.json b/src/plugins/index_pattern_management/kibana.json new file mode 100644 index 00000000000000..d5397a11184aab --- /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 bae0f1d3e23cdb..da482c0c51f0ac 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 ae0be98de63f37..bc97f46c302e32 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 2a8ef10c817cc9..93bb0ead1df4af 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 5714fa33389624..29ab0ebfc3d5f7 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 e7fa13409ab046..32b3e7ee7a133b 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 2b6f008dd928a1..4780fa00ed468d 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 dd4d77a6811717..87c246e8913e53 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 73ca33ae914a9b..3a2910a222cd7c 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 00000000000000..0add1bee84ae02 --- /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 b4007b30cf8cab..21bba92ada4c16 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 939d372b9997f7..aefbd66e50fcf4 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 9b34880cf4fe3a..ee99ea36726727 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 e1689e38dbfe05..9ad9f14ac5659d 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 00000000000000..6fa39d82d10215 --- /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 1876e688c989ac..2f139050e994ab 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/telemetry/common/constants.ts b/src/plugins/telemetry/common/constants.ts index babd009143c5ef..fd32862896528e 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 6eeda080bb3ab9..a874f8dd6202cc 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 00000000000000..a7681e17664272 --- /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 00000000000000..804c8b0ed2026e --- /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 00000000000000..91ede686ded3da --- /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/typings/elastic__node_crypto.d.ts b/src/plugins/telemetry/server/collectors/kibana/index.ts similarity index 91% rename from typings/elastic__node_crypto.d.ts rename to src/plugins/telemetry/server/collectors/kibana/index.ts index 8d4b47da96b738..695d972346b8a5 100644 --- a/typings/elastic__node_crypto.d.ts +++ b/src/plugins/telemetry/server/collectors/kibana/index.ts @@ -17,4 +17,4 @@ * under the License. */ -declare module '@elastic/node-crypto'; +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 00000000000000..ccf6f7b1033c94 --- /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 00000000000000..678237ffb6ea23 --- /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 00000000000000..92e0e40776eb89 --- /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 9df010223542be..443a25749d2003 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 00000000000000..4967e20006ddd7 --- /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 77036b4ea7ddc8..1df6a665e4d766 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 86c6731e11d37b..a17f1b8232a223 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 2b2fc004a84c62..f532c2c8aa2193 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 c590cf8f34ee07..3eaa13572a826e 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 00000000000000..8607dfe486ace3 --- /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 83fbf70c9099ef..c7e3e609490f90 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 9df4ea58e0f075..f2c2f8b4d0b94c 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 792595fd421f67..ecbc41f28003c9 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 1c49ebf40640ed..cac1b0851b92df 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 b1555b76500d0a..3674e39b558d26 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 073cb7d5ac66c2..a32bd76bafa5ab 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 4517313b6fd6e8..5af3cfc5b09289 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 7ee432946f3c87..6cb76b18e24a6f 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 e52b2c85b63fa2..b874459a8e7d3a 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 cc80d0073c9046..639b69cd3d33cb 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 f6edecbbcbd701..560213fc08ff07 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 a43c66c2e08ccf..b4683e47979cea 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 e9090e5b38ef78..a19034d3c8e92f 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 6b1a4dca7b84f5..b844fdfb82256b 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 15e864bfd026df..57f4c7d04019b0 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 fc1cbb51b70a78..42086004a12dc0 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 e2e7c2895093e7..16aaff47f49c31 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 be4c62ab08aa2a..d4e698f655c5e1 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 90b7cb03b7a5b3..a316a087c8bcbc 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 c4b90649aaaaeb..5ffa8088a95467 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 7010f0d53e569e..7d4d2230bd766f 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 6ab5ee2d260a15..02bf680734526c 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 8f63662d928c1a..e609bf9adf790e 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 41672bc192fabe..c5a35cbbd7ab1e 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 9f1aaa54a8ca3e..47b12f4340d429 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 df71e72ee6c617..ad2011513b171c 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 c64b079e4f802c..86c4431b6d5ed1 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 27de9dfe68ee0a..5c6438b4004085 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 824ec4aeb253c2..9f55eb9212dee7 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 c9f53a68b3e83e..ee0fbd8532ce93 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 ead3f8bb006231..361eeba9abdbf8 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 971a62faf7d7c9..4af41f67bc524c 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 18b445b4a26dbd..db9d7d9e3316a1 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 0c967723db8e71..0466c64541e23b 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 b24486a12fd246..9dfeae1815d1a7 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 fb63a598a4faec..c9f83e5b474a68 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 d39d6d07b32d28..1bfa100cbd0f7a 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 b9f89cebd8bf34..4e7a2904584daf 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 899b9c1b5fd6eb..f1963b94dcf954 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 58e67b5064da51..798da09f8e30bf 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 156d50f451b577..6c58c6df26b001 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 4e632da44afc08..05ba5fa9c94198 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 3ff212c43e6e84..bf5661f42a9f5f 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/legacy/core_plugins/management/index.ts b/src/plugins/vis_type_timeseries/server/validation_telemetry/saved_object_type.ts similarity index 59% rename from src/legacy/core_plugins/management/index.ts rename to src/plugins/vis_type_timeseries/server/validation_telemetry/saved_object_type.ts index 4962c948f842f2..77b49e824334fd 100644 --- a/src/legacy/core_plugins/management/index.ts +++ b/src/plugins/vis_type_timeseries/server/validation_telemetry/saved_object_type.ts @@ -17,21 +17,29 @@ * under the License. */ -import { resolve } from 'path'; -import { Legacy } from '../../../../kibana'; +import { flow } from 'lodash'; +import { SavedObjectMigrationFn, SavedObjectsType } from 'kibana/server'; -// 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) => ({}), - }; +const resetCount: SavedObjectMigrationFn = doc => ({ + ...doc, + attributes: { + ...doc.attributes, + failedRequests: 0, + }, +}); - return new kibana.Plugin(config); -} +export const tsvbTelemetrySavedObjectType: SavedObjectsType = { + name: 'tsvb-validation-telemetry', + hidden: false, + namespaceAgnostic: true, + mappings: { + properties: { + failedRequests: { + type: 'long', + }, + }, + }, + migrations: { + '7.7.0': flow(resetCount), + }, +}; diff --git a/src/plugins/vis_type_timeseries/server/validation_telemetry/validation_telemetry_service.ts b/src/plugins/vis_type_timeseries/server/validation_telemetry/validation_telemetry_service.ts index e49664265b8bbb..779d9441df2fd9 100644 --- a/src/plugins/vis_type_timeseries/server/validation_telemetry/validation_telemetry_service.ts +++ b/src/plugins/vis_type_timeseries/server/validation_telemetry/validation_telemetry_service.ts @@ -19,6 +19,7 @@ import { APICaller, CoreSetup, Plugin, PluginInitializerContext } from 'kibana/server'; import { UsageCollectionSetup } from '../../../usage_collection/server'; +import { tsvbTelemetrySavedObjectType } from './saved_object_type'; export interface ValidationTelemetryServiceSetup { logFailedValidation: () => void; @@ -36,6 +37,7 @@ export class ValidationTelemetryService implements Plugin { this.kibanaIndex = config.kibana.index; }); diff --git a/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts b/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts index 02c114bad4e725..26f8278cd3d434 100644 --- a/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts +++ b/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts @@ -150,6 +150,32 @@ describe('migration visualization', () => { expect(aggs[3]).not.toHaveProperty('params.customBucket.params.time_zone'); expect(aggs[2]).not.toHaveProperty('params.time_zone'); }); + + it('should migrate obsolete match_all query', () => { + const migratedDoc = migrate({ + ...doc, + attributes: { + ...doc.attributes, + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ + query: { + match_all: {}, + }, + }), + }, + }, + }); + const migratedSearchSource = JSON.parse( + migratedDoc.attributes.kibanaSavedObjectMeta.searchSourceJSON + ); + + expect(migratedSearchSource).toEqual({ + query: { + query: '', + language: 'kuery', + }, + }); + }); }); }); @@ -181,13 +207,13 @@ describe('migration visualization', () => { }, }); expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "visState": "{}", - }, - "references": Array [], -} -`); + Object { + "attributes": Object { + "visState": "{}", + }, + "references": Array [], + } + `); }); it('skips errors when searchSourceJSON is null', () => { @@ -205,25 +231,25 @@ Object { const migratedDoc = migrate(doc); expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": null, - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); + Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": null, + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", + } + `); }); it('skips errors when searchSourceJSON is undefined', () => { @@ -241,25 +267,25 @@ Object { const migratedDoc = migrate(doc); expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": undefined, - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); + Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": undefined, + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", + } + `); }); it('skips error when searchSourceJSON is not a string', () => { @@ -276,25 +302,25 @@ Object { }; expect(migrate(doc)).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": 123, - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); + Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": 123, + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", + } + `); }); it('skips error when searchSourceJSON is invalid json', () => { @@ -311,25 +337,25 @@ Object { }; expect(migrate(doc)).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{abc123}", - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); + Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{abc123}", + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", + } + `); }); it('skips error when "index" and "filter" is missing from searchSourceJSON', () => { @@ -347,25 +373,25 @@ Object { const migratedDoc = migrate(doc); expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{\\"bar\\":true}", - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); + Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{\\"bar\\":true}", + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", + } + `); }); it('extracts "index" attribute from doc', () => { @@ -383,30 +409,30 @@ Object { const migratedDoc = migrate(doc); expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{\\"bar\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.index\\"}", - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "pattern*", - "name": "kibanaSavedObjectMeta.searchSourceJSON.index", - "type": "index-pattern", - }, - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); + Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{\\"bar\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.index\\"}", + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "pattern*", + "name": "kibanaSavedObjectMeta.searchSourceJSON.index", + "type": "index-pattern", + }, + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", + } + `); }); it('extracts index patterns from the filter', () => { @@ -431,30 +457,30 @@ Object { const migratedDoc = migrate(doc); expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{\\"bar\\":true,\\"filter\\":[{\\"meta\\":{\\"foo\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\\"}}]}", - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "my-index", - "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", - "type": "index-pattern", - }, - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], - "type": "visualization", -} -`); + Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{\\"bar\\":true,\\"filter\\":[{\\"meta\\":{\\"foo\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\\"}}]}", + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "my-index", + "name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index", + "type": "index-pattern", + }, + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + "type": "visualization", + } + `); }); it('extracts index patterns from controls', () => { @@ -482,22 +508,22 @@ Object { const migratedDoc = migrate(doc); expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "foo": true, - "visState": "{\\"bar\\":false,\\"params\\":{\\"controls\\":[{\\"bar\\":true,\\"indexPatternRefName\\":\\"control_0_index_pattern\\"},{\\"foo\\":true}]}}", - }, - "id": "1", - "references": Array [ - Object { - "id": "pattern*", - "name": "control_0_index_pattern", - "type": "index-pattern", - }, - ], - "type": "visualization", -} -`); + Object { + "attributes": Object { + "foo": true, + "visState": "{\\"bar\\":false,\\"params\\":{\\"controls\\":[{\\"bar\\":true,\\"indexPatternRefName\\":\\"control_0_index_pattern\\"},{\\"foo\\":true}]}}", + }, + "id": "1", + "references": Array [ + Object { + "id": "pattern*", + "name": "control_0_index_pattern", + "type": "index-pattern", + }, + ], + "type": "visualization", + } + `); }); it('skips extracting savedSearchId when missing', () => { @@ -513,17 +539,17 @@ Object { const migratedDoc = migrate(doc); expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{}", - }, - "visState": "{}", - }, - "id": "1", - "references": Array [], -} -`); + Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{}", + }, + "visState": "{}", + }, + "id": "1", + "references": Array [], + } + `); }); it('extract savedSearchId from doc', () => { @@ -540,24 +566,24 @@ Object { const migratedDoc = migrate(doc); expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{}", - }, - "savedSearchRefName": "search_0", - "visState": "{}", - }, - "id": "1", - "references": Array [ - Object { - "id": "123", - "name": "search_0", - "type": "search", - }, - ], -} -`); + Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{}", + }, + "savedSearchRefName": "search_0", + "visState": "{}", + }, + "id": "1", + "references": Array [ + Object { + "id": "123", + "name": "search_0", + "type": "search", + }, + ], + } + `); }); it('delete savedSearchId when empty string in doc', () => { @@ -574,17 +600,17 @@ Object { const migratedDoc = migrate(doc); expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "kibanaSavedObjectMeta": Object { - "searchSourceJSON": "{}", - }, - "visState": "{}", - }, - "id": "1", - "references": Array [], -} -`); + Object { + "attributes": Object { + "kibanaSavedObjectMeta": Object { + "searchSourceJSON": "{}", + }, + "visState": "{}", + }, + "id": "1", + "references": Array [], + } + `); }); it('should return a new object if vis is table and has multiple split aggs', () => { @@ -904,12 +930,12 @@ Object { }, }); expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "visState": "{\\"type\\":\\"gauge\\",\\"params\\":{\\"gauge\\":{\\"alignment\\":\\"horizontal\\"}}}", - }, -} -`); + Object { + "attributes": Object { + "visState": "{\\"type\\":\\"gauge\\",\\"params\\":{\\"gauge\\":{\\"alignment\\":\\"horizontal\\"}}}", + }, + } + `); }); it('migrates type = gauge verticalSplit: false to alignment: horizontal', () => { @@ -920,12 +946,12 @@ Object { }); expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "visState": "{\\"type\\":\\"gauge\\",\\"params\\":{\\"gauge\\":{\\"alignment\\":\\"vertical\\"}}}", - }, -} -`); + Object { + "attributes": Object { + "visState": "{\\"type\\":\\"gauge\\",\\"params\\":{\\"gauge\\":{\\"alignment\\":\\"vertical\\"}}}", + }, + } + `); }); it('doesnt migrate type = gauge containing invalid visState object, adds message to log', () => { @@ -936,18 +962,18 @@ Object { }); expect(migratedDoc).toMatchInlineSnapshot(` -Object { - "attributes": Object { - "visState": "{\\"type\\":\\"gauge\\"}", - }, -} -`); + Object { + "attributes": Object { + "visState": "{\\"type\\":\\"gauge\\"}", + }, + } + `); expect(logMsgArr).toMatchInlineSnapshot(` -Array [ - "Exception @ migrateGaugeVerticalSplitToAlignment! TypeError: Cannot read property 'gauge' of undefined", - "Exception @ migrateGaugeVerticalSplitToAlignment! Payload: {\\"type\\":\\"gauge\\"}", -] -`); + Array [ + "Exception @ migrateGaugeVerticalSplitToAlignment! TypeError: Cannot read property 'gauge' of undefined", + "Exception @ migrateGaugeVerticalSplitToAlignment! Payload: {\\"type\\":\\"gauge\\"}", + ] + `); }); describe('filters agg query migration', () => { @@ -1353,4 +1379,85 @@ Array [ expect(timeSeriesParams.series[0].split_filters[0].filter.language).toEqual('lucene'); }); }); + + describe('7.7.0 tsvb opperator typo migration', () => { + const migrate = (doc: any) => + visualizationSavedObjectTypeMigrations['7.7.0']( + doc as Parameters[0], + savedObjectMigrationContext + ); + const generateDoc = (params: any) => ({ + attributes: { + title: 'My Vis', + description: 'This is my super cool vis.', + visState: JSON.stringify({ params }), + uiStateJSON: '{}', + version: 1, + kibanaSavedObjectMeta: { + searchSourceJSON: '{}', + }, + }, + }); + + it('should remove the misspelled opperator key if it exists', () => { + const params = { + type: 'timeseries', + filter: { + query: 'bytes:>1000', + language: 'lucene', + }, + series: [], + gauge_color_rules: [ + { + value: 0, + id: '020e3d50-75a6-11ea-8f61-71579ff7f64d', + gauge: 'rgba(69,39,217,1)', + opperator: 'lt', + }, + ], + }; + const timeSeriesDoc = generateDoc(params); + const migratedtimeSeriesDoc = migrate(timeSeriesDoc); + const migratedParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; + + expect(migratedParams.gauge_color_rules[0]).toMatchInlineSnapshot(` + Object { + "gauge": "rgba(69,39,217,1)", + "id": "020e3d50-75a6-11ea-8f61-71579ff7f64d", + "opperator": "lt", + "value": 0, + } + `); + }); + + it('should not change color rules with the correct spelling', () => { + const params = { + type: 'timeseries', + filter: { + query: 'bytes:>1000', + language: 'lucene', + }, + series: [], + gauge_color_rules: [ + { + value: 0, + id: '020e3d50-75a6-11ea-8f61-71579ff7f64d', + gauge: 'rgba(69,39,217,1)', + opperator: 'lt', + }, + { + value: 0, + id: '020e3d50-75a6-11ea-8f61-71579ff7f64d', + gauge: 'rgba(69,39,217,1)', + operator: 'lt', + }, + ], + }; + const timeSeriesDoc = generateDoc(params); + const migratedtimeSeriesDoc = migrate(timeSeriesDoc); + const migratedParams = JSON.parse(migratedtimeSeriesDoc.attributes.visState).params; + + expect(migratedParams.gauge_color_rules[1]).toEqual(params.gauge_color_rules[1]); + }); + }); }); diff --git a/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts b/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts index 9ee355cbb23cfe..80783e41863eaf 100644 --- a/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts +++ b/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts @@ -19,6 +19,7 @@ import { SavedObjectMigrationFn } from 'kibana/server'; import { cloneDeep, get, omit, has, flow } from 'lodash'; +import { DEFAULT_QUERY_LANGUAGE } from '../../../data/common'; const migrateIndexPattern: SavedObjectMigrationFn = doc => { const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); @@ -98,6 +99,38 @@ const migratePercentileRankAggregation: SavedObjectMigrationFn = doc => { return doc; }; +// [TSVB] Remove stale opperator key +const migrateOperatorKeyTypo: SavedObjectMigrationFn = doc => { + const visStateJSON = get(doc, 'attributes.visState'); + let visState; + + if (visStateJSON) { + try { + visState = JSON.parse(visStateJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + if (visState && visState.type === 'metrics') { + const gaugeColorRules: any[] = get(visState, 'params.gauge_color_rules') || []; + + gaugeColorRules.forEach(colorRule => { + if (colorRule.opperator) { + delete colorRule.opperator; + } + }); + + return { + ...doc, + attributes: { + ...doc.attributes, + visState: JSON.stringify(visState), + }, + }; + } + } + return doc; +}; + // Migrate date histogram aggregation (remove customInterval) const migrateDateHistogramAggregation: SavedObjectMigrationFn = doc => { const visStateJSON = get(doc, 'attributes.visState'); @@ -539,6 +572,40 @@ const migrateTableSplits: SavedObjectMigrationFn = doc => { } }; +const migrateMatchAllQuery: SavedObjectMigrationFn = doc => { + const searchSourceJSON = get(doc, 'attributes.kibanaSavedObjectMeta.searchSourceJSON'); + + if (searchSourceJSON) { + let searchSource: any; + + try { + searchSource = JSON.parse(searchSourceJSON); + } catch (e) { + // Let it go, the data is invalid and we'll leave it as is + } + + if (searchSource.query?.match_all) { + return { + ...doc, + attributes: { + ...doc.attributes, + kibanaSavedObjectMeta: { + searchSourceJSON: JSON.stringify({ + ...searchSource, + query: { + query: '', + language: DEFAULT_QUERY_LANGUAGE, + }, + }), + }, + }, + }; + } + } + + return doc; +}; + export const visualizationSavedObjectTypeMigrations = { /** * We need to have this migration twice, once with a version prior to 7.0.0 once with a version @@ -550,7 +617,7 @@ export const visualizationSavedObjectTypeMigrations = { * in that version. So we apply this twice, once with 6.7.2 and once with 7.0.1 while the backport to 6.7 * only contained the 6.7.2 migration and not the 7.0.1 migration. */ - '6.7.2': flow(removeDateHistogramTimeZones), + '6.7.2': flow(migrateMatchAllQuery, removeDateHistogramTimeZones), '7.0.0': flow( addDocReferences, migrateIndexPattern, @@ -571,4 +638,5 @@ export const visualizationSavedObjectTypeMigrations = { ), '7.3.1': flow(migrateFiltersAggQueryStringQueries), '7.4.2': flow(transformSplitFiltersStringToQueryObject), + '7.7.0': flow(migrateOperatorKeyTypo), }; diff --git a/test/examples/embeddables/list_container.ts b/test/examples/embeddables/list_container.ts index b1b91ad2c37f1b..9e93d479471e8a 100644 --- a/test/examples/embeddables/list_container.ts +++ b/test/examples/embeddables/list_container.ts @@ -57,13 +57,12 @@ export default function({ getService }: PluginFunctionalProviderContext) { expect(text).to.eql(['HELLO WORLD!']); }); - it('searchable container filters multi-task children', async () => { + it('searchable container finds matches in multi-task children', async () => { await testSubjects.setValue('filterTodos', 'earth'); + await testSubjects.click('checkMatchingTodos'); + await testSubjects.click('deleteCheckedTodos'); - await retry.try(async () => { - const tasks = await testSubjects.getVisibleTextAll('multiTaskTodoTask'); - expect(tasks).to.eql(['Watch planet earth']); - }); + await testSubjects.missingOrFail('multiTaskTodoTask'); }); }); } diff --git a/test/functional/apps/discover/_discover.js b/test/functional/apps/discover/_discover.js index 850b2773b5025e..4f357e2993b30a 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/management/_index_pattern_create_delete.js b/test/functional/apps/management/_index_pattern_create_delete.js index a74620b696d1be..616e2297b2f510 100644 --- a/test/functional/apps/management/_index_pattern_create_delete.js +++ b/test/functional/apps/management/_index_pattern_create_delete.js @@ -42,7 +42,7 @@ export default function({ getService, getPageObjects }) { describe('special character handling', () => { it('should handle special charaters in template input', async () => { - await PageObjects.settings.clickOptionalAddNewButton(); + await PageObjects.settings.clickAddNewIndexPatternButton(); await PageObjects.header.waitUntilLoadingHasFinished(); await PageObjects.settings.setIndexPatternField({ indexPatternName: '❤️', diff --git a/test/functional/apps/management/_mgmt_import_saved_objects.js b/test/functional/apps/management/_mgmt_import_saved_objects.js index 53b7e7062ee2d4..2f9d9f9bfb178f 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 00000000000000..ed68b41e8c89ab --- /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/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts index de4917ef2b1b31..f06baeb7a4167b 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 2377c32a80b5b6..00bf87621864ab 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/page_objects/settings_page.ts b/test/functional/page_objects/settings_page.ts index 3f6036f58f0a91..6dcd017335c854 100644 --- a/test/functional/page_objects/settings_page.ts +++ b/test/functional/page_objects/settings_page.ts @@ -326,7 +326,7 @@ export function SettingsPageProvider({ getService, getPageObjects }: FtrProvider await PageObjects.header.waitUntilLoadingHasFinished(); await this.clickKibanaIndexPatterns(); await PageObjects.header.waitUntilLoadingHasFinished(); - await this.clickOptionalAddNewButton(); + await this.clickAddNewIndexPatternButton(); if (!isStandardIndexPattern) { await this.clickCreateNewRollupButton(); } @@ -356,11 +356,8 @@ export function SettingsPageProvider({ getService, getPageObjects }: FtrProvider return await this.getIndexPatternIdFromUrl(); } - // adding a method to check if the create index pattern button is visible when more than 1 index pattern is present - async clickOptionalAddNewButton() { - if (await testSubjects.isDisplayed('createIndexPatternButton')) { - await testSubjects.click('createIndexPatternButton'); - } + async clickAddNewIndexPatternButton() { + await testSubjects.click('createIndexPatternButton'); } async clickCreateNewRollupButton() { diff --git a/test/functional/services/browser.ts b/test/functional/services/browser.ts index 5017947e95d03b..13d2365c07191b 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 157918df874c82..8b57ecd3c82350 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 46d81f1737a55f..aa6e364d0a09d0 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 e571a1a7e55512..b0724488cb5db8 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 382543822d8ac1..fc0b5bbb787c81 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 d3f66d708603c5..6c430e34d5e29b 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/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/app.tsx b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/app.tsx index 54d13efe4d7909..2ecde823dc4dfc 100644 --- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/app.tsx +++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/app.tsx @@ -18,21 +18,11 @@ */ import { EuiTab } from '@elastic/eui'; import React, { Component } from 'react'; -import { CoreStart } from 'src/core/public'; import { EmbeddableStart } from 'src/plugins/embeddable/public'; -import { UiActionsService } from '../../../../../../../../src/plugins/ui_actions/public'; import { DashboardContainerExample } from './dashboard_container_example'; -import { Start as InspectorStartContract } from '../../../../../../../../src/plugins/inspector/public'; export interface AppProps { - getActions: UiActionsService['getTriggerCompatibleActions']; - getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']; - getAllEmbeddableFactories: EmbeddableStart['getEmbeddableFactories']; - overlays: CoreStart['overlays']; - notifications: CoreStart['notifications']; - inspector: InspectorStartContract; - SavedObjectFinder: React.ComponentType; - I18nContext: CoreStart['i18n']['Context']; + embeddableServices: EmbeddableStart; } export class App extends Component { @@ -72,29 +62,17 @@ export class App extends Component { public render() { return ( - -
-
{this.renderTabs()}
- {this.getContentsForTab()} -
-
+
+
{this.renderTabs()}
+ {this.getContentsForTab()} +
); } private getContentsForTab() { switch (this.state.selectedTabId) { case 'dashboardContainer': { - return ( - - ); + return ; } } } diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/dashboard_container_example.tsx b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/dashboard_container_example.tsx index fd07416cadbc5d..16c2840d6a32e8 100644 --- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/dashboard_container_example.tsx +++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/app/dashboard_container_example.tsx @@ -19,32 +19,17 @@ import React from 'react'; import { EuiButton, EuiLoadingChart } from '@elastic/eui'; import { ContainerOutput } from 'src/plugins/embeddable/public'; -import { - ErrorEmbeddable, - ViewMode, - isErrorEmbeddable, - EmbeddablePanel, - EmbeddableStart, -} from '../embeddable_api'; +import { ErrorEmbeddable, ViewMode, isErrorEmbeddable, EmbeddableStart } from '../embeddable_api'; import { DASHBOARD_CONTAINER_TYPE, DashboardContainer, DashboardContainerInput, } from '../../../../../../../../src/plugins/dashboard/public'; -import { CoreStart } from '../../../../../../../../src/core/public'; import { dashboardInput } from './dashboard_input'; -import { Start as InspectorStartContract } from '../../../../../../../../src/plugins/inspector/public'; -import { UiActionsService } from '../../../../../../../../src/plugins/ui_actions/public'; interface Props { - getActions: UiActionsService['getTriggerCompatibleActions']; - getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']; - getAllEmbeddableFactories: EmbeddableStart['getEmbeddableFactories']; - overlays: CoreStart['overlays']; - notifications: CoreStart['notifications']; - inspector: InspectorStartContract; - SavedObjectFinder: React.ComponentType; + embeddableServices: EmbeddableStart; } interface State { @@ -67,7 +52,7 @@ export class DashboardContainerExample extends React.Component { public async componentDidMount() { this.mounted = true; - const dashboardFactory = this.props.getEmbeddableFactory< + const dashboardFactory = this.props.embeddableServices.getEmbeddableFactory< DashboardContainerInput, ContainerOutput, DashboardContainer @@ -99,6 +84,7 @@ export class DashboardContainerExample extends React.Component { }; public render() { + const { embeddableServices } = this.props; return (

Dashboard Container

@@ -108,16 +94,7 @@ export class DashboardContainerExample extends React.Component { {!this.state.loaded || !this.container ? ( ) : ( - + )}
); diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/plugin.tsx b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/plugin.tsx index 18ceec652392d1..e5f5faa6ac361d 100644 --- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/plugin.tsx +++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/public/np_ready/public/plugin.tsx @@ -33,7 +33,6 @@ const REACT_ROOT_ID = 'embeddableExplorerRoot'; import { SayHelloAction, createSendMessageAction } from './embeddable_api'; import { App } from './app'; -import { getSavedObjectFinder } from '../../../../../../../src/plugins/saved_objects/public'; import { EmbeddableStart, EmbeddableSetup, @@ -78,19 +77,7 @@ export class EmbeddableExplorerPublicPlugin plugins.__LEGACY.onRenderComplete(() => { const root = document.getElementById(REACT_ROOT_ID); - ReactDOM.render( - , - root - ); + ReactDOM.render(, root); }); } diff --git a/test/scripts/jenkins_visual_regression.sh b/test/scripts/jenkins_visual_regression.sh index 4fdd197147eac0..c6fefd45b005d1 100755 --- a/test/scripts/jenkins_visual_regression.sh +++ b/test/scripts/jenkins_visual_regression.sh @@ -12,7 +12,7 @@ tar -xzf "$linuxBuild" -C "$installDir" --strip=1 echo " -> running visual regression tests from kibana directory" checks-reporter-with-killswitch "X-Pack visual regression tests" \ - yarn percy exec -t 500 \ + yarn percy exec -t 500 -- -- \ node scripts/functional_tests \ --debug --bail \ --kibana-install-dir "$installDir" \ diff --git a/test/scripts/jenkins_xpack_build_kibana.sh b/test/scripts/jenkins_xpack_build_kibana.sh index 777d98080e407e..962d2794f712f4 100755 --- a/test/scripts/jenkins_xpack_build_kibana.sh +++ b/test/scripts/jenkins_xpack_build_kibana.sh @@ -7,6 +7,7 @@ echo " -> building kibana platform plugins" node scripts/build_kibana_platform_plugins \ --scan-dir "$XPACK_DIR/test/plugin_functional/plugins" \ --scan-dir "$XPACK_DIR/test/functional_with_es_ssl/fixtures/plugins" \ + --scan-dir "$XPACK_DIR/test/plugin_api_integration/plugins" \ --verbose; # doesn't persist, also set in kibanaPipeline.groovy diff --git a/test/scripts/jenkins_xpack_visual_regression.sh b/test/scripts/jenkins_xpack_visual_regression.sh index 73e92da3bad635..96521ccc8f7873 100755 --- a/test/scripts/jenkins_xpack_visual_regression.sh +++ b/test/scripts/jenkins_xpack_visual_regression.sh @@ -14,7 +14,7 @@ tar -xzf "$linuxBuild" -C "$installDir" --strip=1 echo " -> running visual regression tests from x-pack directory" cd "$XPACK_DIR" checks-reporter-with-killswitch "X-Pack visual regression tests" \ - yarn percy exec -t 500 \ + yarn percy exec -t 500 -- -- \ node scripts/functional_tests \ --debug --bail \ --kibana-install-dir "$installDir" \ 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 46754c8c7cb6b1..a8d3b843a1f3d3 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 7bdc6aebbd9a0f..e4b656ae8160d7 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 0438842f7af10d..92f66f698f0446 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 4925ffba310b57..dd9b48d3127254 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 b1959e4d68aa43..30c772bf5f634a 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/app/Settings/CustomizeUI/CustomLink/Title.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/Title.tsx index 17ec42b3e20161..07af7b0c0e7db8 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/Title.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/Title.tsx @@ -28,7 +28,7 @@ export const Title = () => ( 'xpack.apm.settings.customizeUI.customLink.info', { defaultMessage: - "These links will be shown in the 'Actions' context menu for the transaction detail." + 'These links will be shown in the Actions context menu for transactions.' } )} /> diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/index.tsx index 1cd1298fdd5492..350f2185fb3c8a 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/index.tsx @@ -14,8 +14,8 @@ export const CustomizeUI = () => { <>

- {i18n.translate('xpack.apm.settings.customizeUI', { - defaultMessage: 'Customize UI' + {i18n.translate('xpack.apm.settings.customizeApp', { + defaultMessage: 'Customize app' })}

diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/index.tsx index f33bb17decd4e2..2bb85876686bf2 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/index.tsx @@ -57,8 +57,8 @@ export const Settings: React.FC = props => { isSelected: pathname === '/settings/apm-indices' }, { - name: i18n.translate('xpack.apm.settings.customizeUI', { - defaultMessage: 'Customize UI' + name: i18n.translate('xpack.apm.settings.customizeApp', { + defaultMessage: 'Customize app' }), id: '3', href: getAPMHref('/settings/customize-ui', search), 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 9bfc5936a555ee..b7e23c2979cb89 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 52befe37ffdae9..bd00bcf600ffeb 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/apm/public/components/shared/TransactionActionMenu/CustomLink/index.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.test.tsx index 2dab8d63f99b26..9d1eeb9a3136d9 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.test.tsx @@ -28,7 +28,7 @@ describe('Custom links', () => { ); expectTextsInDocument(component, [ - 'No custom links found. Set up your own custom links i.e. a link to a specific Dashboard or external link.' + 'No custom links found. Set up your own custom links, e.g., a link to a specific Dashboard or external link.' ]); expectTextsNotInDocument(component, ['Create']); }); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.tsx index b32d8f0d9582cb..38b672a181fceb 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/CustomLink/index.tsx @@ -55,7 +55,7 @@ export const CustomLink = ({ {i18n.translate('xpack.apm.customLink.empty', { defaultMessage: - 'No custom links found. Set up your own custom links i.e. a link to a specific Dashboard or external link.' + 'No custom links found. Set up your own custom links, e.g., a link to a specific Dashboard or external link.' })} diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx index 048ed662ec502d..e3fbcf8485d541 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx @@ -85,7 +85,13 @@ export const TransactionActionMenu: FunctionComponent = ({ urlParams }); + const closePopover = () => { + setIsActionPopoverOpen(false); + setIsCustomLinksPopoverOpen(false); + }; + const toggleCustomLinkFlyout = () => { + closePopover(); setIsCustomLinkFlyoutOpen(isOpen => !isOpen); }; @@ -111,10 +117,7 @@ export const TransactionActionMenu: FunctionComponent = ({ )} { - setIsActionPopoverOpen(false); - setIsCustomLinksPopoverOpen(false); - }} + closePopover={closePopover} isOpen={isActionPopoverOpen} anchorPosition="downRight" button={ diff --git a/x-pack/legacy/plugins/canvas/.storybook/webpack.config.js b/x-pack/legacy/plugins/canvas/.storybook/webpack.config.js index 85cb6d45c595da..cc74faeac6a964 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/plugins/ml/public/application/datavisualizer/file_based/components/import_progress/index.js b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata/demo_rows_types.ts similarity index 78% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_progress/index.js rename to x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata/demo_rows_types.ts index f75d869f4667c2..e92dc79fba8c31 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_progress/index.js +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/functions/server/demodata/demo_rows_types.ts @@ -4,4 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -export { ImportProgress, IMPORT_STATUS } from './import_progress'; +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 02f8efcfde95d3..58a2354b5cf384 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 826c49d328f214..5cebae5bb669f7 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 eba0b47f7dc13b..88d2b904e6cb3f 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 caedbfdec5be4c..35a5b86f752dce 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 5ab6a908641dea..d8e4d51706be96 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/public/application.tsx b/x-pack/legacy/plugins/canvas/public/application.tsx index e26157aadebcb1..79b3918fef99be 100644 --- a/x-pack/legacy/plugins/canvas/public/application.tsx +++ b/x-pack/legacy/plugins/canvas/public/application.tsx @@ -26,9 +26,24 @@ import { getDocumentationLinks } from './lib/documentation_links'; import { HelpMenu } from './components/help_menu/help_menu'; import { createStore } from './store'; +import { VALUE_CLICK_TRIGGER, ActionByType } from '../../../../../src/plugins/ui_actions/public'; +/* eslint-disable */ +import { ACTION_VALUE_CLICK } from '../../../../../src/plugins/data/public/actions/value_click_action'; +/* eslint-enable */ + import { CapabilitiesStrings } from '../i18n'; const { ReadOnlyBadge: strings } = CapabilitiesStrings; +let restoreAction: ActionByType | undefined; +const emptyAction = { + id: 'empty-action', + type: '', + getDisplayName: () => 'empty action', + getIconType: () => undefined, + isCompatible: async () => true, + execute: async () => undefined, +} as ActionByType; + export const renderApp = ( coreStart: CoreStart, plugins: CanvasStartDeps, @@ -94,13 +109,30 @@ export const initializeCanvas = async ( }, }); + // TODO: We need this to disable the filtering modal from popping up in lens embeds until + // they honor the disableTriggers parameter + const action = startPlugins.uiActions.getAction(ACTION_VALUE_CLICK); + + if (action) { + restoreAction = action; + + startPlugins.uiActions.detachAction(VALUE_CLICK_TRIGGER, action.id); + startPlugins.uiActions.attachAction(VALUE_CLICK_TRIGGER, emptyAction); + } + return canvasStore; }; -export const teardownCanvas = (coreStart: CoreStart) => { +export const teardownCanvas = (coreStart: CoreStart, startPlugins: CanvasStartDeps) => { destroyRegistries(); resetInterpreter(); + startPlugins.uiActions.detachAction(VALUE_CLICK_TRIGGER, emptyAction.id); + if (restoreAction) { + startPlugins.uiActions.attachAction(VALUE_CLICK_TRIGGER, restoreAction); + restoreAction = undefined; + } + coreStart.chrome.setBadge(undefined); coreStart.chrome.setHelpExtension(undefined); }; diff --git a/x-pack/legacy/plugins/canvas/public/legacy.ts b/x-pack/legacy/plugins/canvas/public/legacy.ts index 9bccc958f72631..a6caa1985325ef 100644 --- a/x-pack/legacy/plugins/canvas/public/legacy.ts +++ b/x-pack/legacy/plugins/canvas/public/legacy.ts @@ -27,6 +27,7 @@ const shimSetupPlugins: CanvasSetupDeps = { const shimStartPlugins: CanvasStartDeps = { ...npStart.plugins, expressions: npStart.plugins.expressions, + uiActions: npStart.plugins.uiActions, __LEGACY: { // ToDo: Copy directly into canvas absoluteToParsedUrl, diff --git a/x-pack/legacy/plugins/canvas/public/plugin.tsx b/x-pack/legacy/plugins/canvas/public/plugin.tsx index f4a3aed28a0a45..d9e5e6b4b084bf 100644 --- a/x-pack/legacy/plugins/canvas/public/plugin.tsx +++ b/x-pack/legacy/plugins/canvas/public/plugin.tsx @@ -10,6 +10,7 @@ import { HomePublicPluginSetup } from '../../../../../src/plugins/home/public'; import { initLoadingIndicator } from './lib/loading_indicator'; import { featureCatalogueEntry } from './feature_catalogue_entry'; import { ExpressionsSetup, ExpressionsStart } from '../../../../../src/plugins/expressions/public'; +import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; // @ts-ignore untyped local import { argTypeSpecs } from './expression_types/arg_types'; import { transitions } from './transitions'; @@ -31,6 +32,7 @@ export interface CanvasSetupDeps { export interface CanvasStartDeps { expressions: ExpressionsStart; + uiActions: UiActionsStart; __LEGACY: { absoluteToParsedUrl: (url: string, basePath: string) => any; formatMsg: any; @@ -70,7 +72,7 @@ export class CanvasPlugin return () => { unmount(); - teardownCanvas(coreStart); + teardownCanvas(coreStart, depsStart); }; }, }); 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 00000000000000..3571448c11aa98 --- /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/lens/public/editor_frame_service/service.test.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_service/service.test.tsx index 47fd810bb4c531..42a1fcc055a1ed 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_service/service.test.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_service/service.test.tsx @@ -17,7 +17,6 @@ import { CoreSetup } from 'kibana/public'; jest.mock('ui/new_platform'); // mock away actual dependencies to prevent all of it being loaded -jest.mock('../../../../../../src/legacy/core_plugins/interpreter/public/registries', () => {}); jest.mock('./embeddable/embeddable_factory', () => ({ EmbeddableFactory: class Mock {}, })); diff --git a/x-pack/legacy/plugins/lens/public/helpers/url_helper.test.ts b/x-pack/legacy/plugins/lens/public/helpers/url_helper.test.ts index 9c59c9a96d00ff..ef960fb52952b5 100644 --- a/x-pack/legacy/plugins/lens/public/helpers/url_helper.test.ts +++ b/x-pack/legacy/plugins/lens/public/helpers/url_helper.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -jest.mock('../legacy_imports', () => ({ +jest.mock('../../../../../../src/plugins/dashboard/public', () => ({ DashboardConstants: { ADD_EMBEDDABLE_ID: 'addEmbeddableId', ADD_EMBEDDABLE_TYPE: 'addEmbeddableType', diff --git a/x-pack/legacy/plugins/lens/public/helpers/url_helper.ts b/x-pack/legacy/plugins/lens/public/helpers/url_helper.ts index fca44195b98c42..3495c15118ce77 100644 --- a/x-pack/legacy/plugins/lens/public/helpers/url_helper.ts +++ b/x-pack/legacy/plugins/lens/public/helpers/url_helper.ts @@ -5,7 +5,7 @@ */ import { parseUrl, stringify } from 'query-string'; -import { DashboardConstants } from '../legacy_imports'; +import { DashboardConstants } from '../../../../../../src/plugins/dashboard/public'; type UrlVars = Record; diff --git a/x-pack/legacy/plugins/lens/public/legacy.ts b/x-pack/legacy/plugins/lens/public/legacy.ts index b7d47644c7f31c..3b7b6a7a1b5108 100644 --- a/x-pack/legacy/plugins/lens/public/legacy.ts +++ b/x-pack/legacy/plugins/lens/public/legacy.ts @@ -5,7 +5,6 @@ */ import { npSetup, npStart } from 'ui/new_platform'; -import { visualizations } from './legacy_imports'; export * from './types'; @@ -14,6 +13,5 @@ import { plugin } from './index'; const pluginInstance = plugin(); pluginInstance.setup(npSetup.core, { ...npSetup.plugins, - __LEGACY: { visualizations }, }); pluginInstance.start(npStart.core, npStart.plugins); diff --git a/x-pack/legacy/plugins/lens/public/legacy_imports.ts b/x-pack/legacy/plugins/lens/public/legacy_imports.ts deleted file mode 100644 index 0f37e460e2957b..00000000000000 --- a/x-pack/legacy/plugins/lens/public/legacy_imports.ts +++ /dev/null @@ -1,10 +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 { npSetup } from 'ui/new_platform'; -export const { visualizations } = npSetup.plugins; -export { VisualizationsSetup } from '../../../../../src/plugins/visualizations/public'; -export { DashboardConstants } from '../../../../../src/plugins/dashboard/public'; diff --git a/x-pack/legacy/plugins/lens/public/plugin.tsx b/x-pack/legacy/plugins/lens/public/plugin.tsx index 45817fdc3c05f2..b426a12d07f9b9 100644 --- a/x-pack/legacy/plugins/lens/public/plugin.tsx +++ b/x-pack/legacy/plugins/lens/public/plugin.tsx @@ -15,7 +15,9 @@ import { AppMountParameters, CoreSetup, CoreStart } from 'src/core/public'; import { DataPublicPluginSetup, DataPublicPluginStart } from 'src/plugins/data/public'; import { EmbeddableSetup, EmbeddableStart } from 'src/plugins/embeddable/public'; import { ExpressionsSetup, ExpressionsStart } from 'src/plugins/expressions/public'; +import { VisualizationsSetup } from 'src/plugins/visualizations/public'; import { KibanaLegacySetup } from 'src/plugins/kibana_legacy/public'; +import { DashboardConstants } from '../../../../../src/plugins/dashboard/public'; import { Storage } from '../../../../../src/plugins/kibana_utils/public'; import { EditorFrameService } from './editor_frame_service'; import { IndexPatternDatasource } from './indexpattern_datasource'; @@ -37,16 +39,13 @@ import { NOT_INTERNATIONALIZED_PRODUCT_NAME } from '../../../../plugins/lens/com import { addEmbeddableToDashboardUrl, getUrlVars } from './helpers'; import { EditorFrameStart } from './types'; import { getLensAliasConfig } from './vis_type_alias'; -import { VisualizationsSetup, DashboardConstants } from './legacy_imports'; export interface LensPluginSetupDependencies { kibanaLegacy: KibanaLegacySetup; expressions: ExpressionsSetup; data: DataPublicPluginSetup; embeddable: EmbeddableSetup; - __LEGACY: { - visualizations: VisualizationsSetup; - }; + visualizations: VisualizationsSetup; } export interface LensPluginStartDependencies { @@ -77,13 +76,7 @@ export class LensPlugin { setup( core: CoreSetup, - { - kibanaLegacy, - expressions, - data, - embeddable, - __LEGACY: { visualizations }, - }: LensPluginSetupDependencies + { kibanaLegacy, expressions, data, embeddable, visualizations }: LensPluginSetupDependencies ) { const editorFrameSetupInterface = this.editorFrameService.setup(core, { data, diff --git a/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts b/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts index b4a8ff90c35123..34f8c30b51874b 100644 --- a/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts +++ b/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts @@ -5,59 +5,4 @@ */ /* eslint-disable @typescript-eslint/consistent-type-definitions */ -import { Filter, Query, TimeRange } from 'src/plugins/data/public'; -import { AnyAction } from 'redux'; -import { LAYER_TYPE } from '../../common/constants'; -import { DataMeta, MapFilters } from '../../common/descriptor_types'; -import { - MapCenterAndZoom, - MapRefreshConfig, -} from '../../../../../plugins/maps/common/descriptor_types'; - -export type SyncContext = { - startLoading(dataId: string, requestToken: symbol, meta: DataMeta): void; - stopLoading(dataId: string, requestToken: symbol, data: unknown, meta: DataMeta): void; - onLoadError(dataId: string, requestToken: symbol, errorMessage: string): void; - updateSourceData(newData: unknown): void; - isRequestStillActive(dataId: string, requestToken: symbol): boolean; - registerCancelCallback(requestToken: symbol, callback: () => void): void; - dataFilters: MapFilters; -}; - -export function updateSourceProp( - layerId: string, - propName: string, - value: unknown, - newLayerType?: LAYER_TYPE -): void; - -export function setGotoWithCenter(config: MapCenterAndZoom): AnyAction; - -export function replaceLayerList(layerList: unknown[]): AnyAction; - -export type QueryGroup = { - filters: Filter[]; - query?: Query; - timeFilters?: TimeRange; - refresh?: boolean; -}; - -export function setQuery(query: QueryGroup): AnyAction; - -export function setRefreshConfig(config: MapRefreshConfig): AnyAction; - -export function disableScrollZoom(): AnyAction; - -export function disableInteractive(): AnyAction; - -export function disableTooltipControl(): AnyAction; - -export function hideToolbarOverlay(): AnyAction; - -export function hideLayerControl(): AnyAction; - -export function hideViewControl(): AnyAction; - -export function setHiddenLayers(hiddenLayerIds: string[]): AnyAction; - -export function addLayerWithoutDataSync(layerDescriptor: unknown): AnyAction; +export * from '../../../../../plugins/maps/public/actions/map_actions'; diff --git a/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.js b/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.js index 8fc32aef547701..5e497ff0736b2c 100644 --- a/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.js +++ b/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.js @@ -4,10 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ import _ from 'lodash'; -import { KibanaTilemapSource } from '../layers/sources/kibana_tilemap_source'; -import { EMSTMSSource } from '../layers/sources/ems_tms_source'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { KibanaTilemapSource } from '../../../../../plugins/maps/public/layers/sources/kibana_tilemap_source'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { EMSTMSSource } from '../../../../../plugins/maps/public/layers/sources/ems_tms_source'; import { getInjectedVarFunc } from '../kibana_services'; -import { getKibanaTileMap } from '../meta'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { getKibanaTileMap } from '../../../../../plugins/maps/public/meta'; export function getInitialLayers(layerListJSON, initialLayers = []) { if (layerListJSON) { diff --git a/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.test.js b/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.test.js index f41ed26b2a05d3..5334beaaf714ac 100644 --- a/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.test.js +++ b/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.test.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -jest.mock('../meta', () => { +jest.mock('../../../../../plugins/maps/public/meta', () => { return {}; }); jest.mock('../kibana_services'); @@ -32,7 +32,7 @@ describe('Saved object has layer list', () => { describe('kibana.yml configured with map.tilemap.url', () => { beforeAll(() => { - require('../meta').getKibanaTileMap = () => { + require('../../../../../plugins/maps/public/meta').getKibanaTileMap = () => { return { url: 'myTileUrl', }; @@ -62,7 +62,7 @@ describe('kibana.yml configured with map.tilemap.url', () => { describe('EMS is enabled', () => { beforeAll(() => { - require('../meta').getKibanaTileMap = () => { + require('../../../../../plugins/maps/public/meta').getKibanaTileMap = () => { return null; }; require('../kibana_services').getInjectedVarFunc = () => key => { @@ -106,7 +106,7 @@ describe('EMS is enabled', () => { describe('EMS is not enabled', () => { beforeAll(() => { - require('../meta').getKibanaTileMap = () => { + require('../../../../../plugins/maps/public/meta').getKibanaTileMap = () => { return null; }; diff --git a/x-pack/legacy/plugins/maps/public/angular/services/saved_gis_map.js b/x-pack/legacy/plugins/maps/public/angular/services/saved_gis_map.js index f846d3d4a617f0..990a0613da681c 100644 --- a/x-pack/legacy/plugins/maps/public/angular/services/saved_gis_map.js +++ b/x-pack/legacy/plugins/maps/public/angular/services/saved_gis_map.js @@ -17,7 +17,8 @@ import { getFilters, } from '../../selectors/map_selectors'; import { getIsLayerTOCOpen, getOpenTOCDetails } from '../../selectors/ui_selectors'; -import { convertMapExtentToPolygon } from '../../elasticsearch_geo_utils'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { convertMapExtentToPolygon } from '../../../../../../plugins/maps/public/elasticsearch_geo_utils'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { copyPersistentState } from '../../../../../../plugins/maps/public/reducers/util'; import { extractReferences, injectReferences } from '../../../common/migrations/references'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts b/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts index 00a9400109dc19..8689d882971719 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts +++ b/x-pack/legacy/plugins/maps/public/connected_components/gis_map/index.d.ts @@ -6,7 +6,8 @@ import React from 'react'; import { Filter } from 'src/plugins/data/public'; -import { RenderToolTipContent } from '../../layers/tooltips/tooltip_property'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { RenderToolTipContent } from '../../../../../../plugins/maps/public/layers/tooltips/tooltip_property'; export const GisMap: React.ComponentType<{ addFilters: ((filters: Filter[]) => void) | null; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/view.js b/x-pack/legacy/plugins/maps/public/connected_components/gis_map/view.js index 97139103ab7c13..358313b8f5b6dd 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/gis_map/view.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/gis_map/view.js @@ -13,7 +13,8 @@ import { LayerPanel } from '../layer_panel/index'; import { AddLayerPanel } from '../layer_addpanel/index'; import { EuiFlexGroup, EuiFlexItem, EuiCallOut } from '@elastic/eui'; import { ExitFullScreenButton } from 'ui/exit_full_screen'; -import { getIndexPatternsFromIds } from '../../index_pattern_util'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { getIndexPatternsFromIds } from '../../../../../../plugins/maps/public/index_pattern_util'; import { ES_GEO_FIELD_TYPE } from '../../../common/constants'; import { indexPatterns as indexPatternsUtils } from '../../../../../../../src/plugins/data/public'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/import_editor/view.js b/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/import_editor/view.js index 762409b2562860..cb20d80733c33b 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/import_editor/view.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/import_editor/view.js @@ -7,7 +7,8 @@ import React, { Fragment } from 'react'; import { EuiSpacer, EuiPanel, EuiButtonEmpty } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { uploadLayerWizardConfig } from '../../../layers/sources/client_file_source'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { uploadLayerWizardConfig } from '../../../../../../../plugins/maps/public/layers/sources/client_file_source'; export const ImportEditor = ({ clearSource, isIndexingTriggered, ...props }) => { const editorProperties = getEditorProperties({ isIndexingTriggered, ...props }); diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/source_select.js b/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/source_select.js index b34a432bec88c0..67cc17ebaa2241 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/source_select.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/source_select.js @@ -5,7 +5,8 @@ */ import React, { Fragment } from 'react'; -import { getLayerWizards } from '../../../layers/layer_wizard_registry'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { getLayerWizards } from '../../../../../../../plugins/maps/public/layers/layer_wizard_registry'; import { EuiTitle, EuiSpacer, EuiCard, EuiIcon } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import _ from 'lodash'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/join_expression.js b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/join_expression.js index f7edcf6e85e255..6c080ace4442ab 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/join_expression.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/join_expression.js @@ -16,9 +16,11 @@ import { EuiFormHelpText, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { SingleFieldSelect } from '../../../../components/single_field_select'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { SingleFieldSelect } from '../../../../../../../../plugins/maps/public/components/single_field_select'; import { FormattedMessage } from '@kbn/i18n/react'; -import { getTermsFields } from '../../../../index_pattern_util'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { getTermsFields } from '../../../../../../../../plugins/maps/public/index_pattern_util'; import { getIndexPatternService, getIndexPatternSelectComponent, diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/metrics_expression.js b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/metrics_expression.js index 0944d0e602c2fa..c6a79a398f9af2 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/metrics_expression.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/metrics_expression.js @@ -14,7 +14,8 @@ import { EuiFormErrorText, EuiFormHelpText, } from '@elastic/eui'; -import { MetricsEditor } from '../../../../components/metrics_editor'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { MetricsEditor } from '../../../../../../../../plugins/maps/public/components/metrics_editor'; import { FormattedMessage } from '@kbn/i18n/react'; import { AGG_TYPE } from '../../../../../common/constants'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/metrics_expression.test.js b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/metrics_expression.test.js index e4e3776c8e92ca..d8bf8622494484 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/metrics_expression.test.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/join_editor/resources/metrics_expression.test.js @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -jest.mock('../../../../components/metrics_editor', () => ({ +jest.mock('../../../../../../../../plugins/maps/public/components/metric_editor', () => ({ MetricsEditor: () => { return
mockMetricsEditor
; }, diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js index eb23607aa2150c..bd274509436382 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/layer_settings/layer_settings.js @@ -8,7 +8,8 @@ import React, { Fragment } from 'react'; import { EuiTitle, EuiPanel, EuiFormRow, EuiFieldText, EuiSpacer } from '@elastic/eui'; -import { ValidatedRange } from '../../../components/validated_range'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { ValidatedRange } from '../../../../../../../plugins/maps/public/components/validated_range'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { ValidatedDualRange } from '../../../../../../../../src/plugins/kibana_react/public'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.d.ts b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.d.ts index 6d1d076c723adf..cf4fdc7be70c68 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.d.ts +++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.d.ts @@ -5,10 +5,4 @@ */ /* eslint-disable @typescript-eslint/consistent-type-definitions */ -import { LAYER_TYPE } from '../../../common/constants'; - -export type OnSourceChangeArgs = { - propName: string; - value: unknown; - newLayerType?: LAYER_TYPE; -}; +export * from '../../../../../../plugins/maps/public/connected_components/layer_panel/view'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js b/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js index 416af955810588..7063c50edad6a0 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/map/features_tooltip/feature_geometry_filter_form.js @@ -8,7 +8,8 @@ import React, { Component, Fragment } from 'react'; import { EuiIcon } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { createSpatialFilterWithGeometry } from '../../../elasticsearch_geo_utils'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { createSpatialFilterWithGeometry } from '../../../../../../../plugins/maps/public/elasticsearch_geo_utils'; import { GEO_JSON_TYPE } from '../../../../common/constants'; import { GeometryFilterForm } from '../../../components/geometry_filter_form'; import { UrlOverflowService } from 'ui/error_url_overflow'; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js index 99abe5d108b5a9..df2988d399c5bb 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/draw_control/draw_control.js @@ -16,7 +16,8 @@ import { createSpatialFilterWithGeometry, getBoundingBoxGeometry, roundCoordinates, -} from '../../../../elasticsearch_geo_utils'; + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../../../../../plugins/maps/public/elasticsearch_geo_utils'; import { DrawTooltip } from './draw_tooltip'; const mbDrawModes = MapboxDraw.modes; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/utils.js b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/utils.js index a2850d2bb6c23b..a1d1341b7c4f7e 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/utils.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/utils.js @@ -5,7 +5,13 @@ */ import _ from 'lodash'; -import { RGBAImage } from './image_utils'; +import { + loadSpriteSheetImageData, + addSpriteSheetToMapFromImageData, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../../../../plugins/maps/public/connected_components/map/mb/utils'; + +export { loadSpriteSheetImageData, addSpriteSheetToMapFromImageData }; export function removeOrphanedSourcesAndLayers(mbMap, layerList) { const mbStyle = mbMap.getStyle(); @@ -95,62 +101,7 @@ export function syncLayerOrderForSingleLayer(mbMap, layerList) { }); } -function getImageData(img) { - const canvas = window.document.createElement('canvas'); - const context = canvas.getContext('2d'); - if (!context) { - throw new Error('failed to create canvas 2d context'); - } - canvas.width = img.width; - canvas.height = img.height; - context.drawImage(img, 0, 0, img.width, img.height); - return context.getImageData(0, 0, img.width, img.height); -} - -export async function loadSpriteSheetImageData(imgUrl) { - return new Promise((resolve, reject) => { - const image = new Image(); - if (isCrossOriginUrl(imgUrl)) { - image.crossOrigin = 'Anonymous'; - } - image.onload = el => { - const imgData = getImageData(el.currentTarget); - resolve(imgData); - }; - image.onerror = e => { - reject(e); - }; - image.src = imgUrl; - }); -} - -export function addSpriteSheetToMapFromImageData(json, imgData, mbMap) { - for (const imageId in json) { - if (!(json.hasOwnProperty(imageId) && !mbMap.hasImage(imageId))) { - continue; - } - const { width, height, x, y, sdf, pixelRatio } = json[imageId]; - if (typeof width !== 'number' || typeof height !== 'number') { - continue; - } - - const data = new RGBAImage({ width, height }); - RGBAImage.copy(imgData, data, { x, y }, { x: 0, y: 0 }, { width, height }); - mbMap.addImage(imageId, data, { pixelRatio, sdf }); - } -} - export async function addSpritesheetToMap(json, imgUrl, mbMap) { const imgData = await loadSpriteSheetImageData(imgUrl); addSpriteSheetToMapFromImageData(json, imgData, mbMap); } - -function isCrossOriginUrl(url) { - const a = window.document.createElement('a'); - a.href = url; - return ( - a.protocol !== window.document.location.protocol || - a.host !== window.document.location.host || - a.port !== window.document.location.port - ); -} diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/view.js b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/view.js index 2995ea039e7a8b..fedc1902d80a25 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/view.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/view.js @@ -12,7 +12,8 @@ import { removeOrphanedSourcesAndLayers, addSpritesheetToMap, } from './utils'; -import { getGlyphUrl, isRetina } from '../../../meta'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { getGlyphUrl, isRetina } from '../../../../../../../plugins/maps/public/meta'; import { DECIMAL_DEGREES_PRECISION, ZOOM_PRECISION } from '../../../../common/constants'; import mapboxgl from 'mapbox-gl/dist/mapbox-gl-csp'; import mbWorkerUrl from '!!file-loader!mapbox-gl/dist/mapbox-gl-csp-worker'; @@ -23,7 +24,11 @@ import sprites1 from '@elastic/maki/dist/sprite@1.png'; import sprites2 from '@elastic/maki/dist/sprite@2.png'; import { DrawControl } from './draw_control'; import { TooltipControl } from './tooltip_control'; -import { clampToLatBounds, clampToLonBounds } from '../../../elasticsearch_geo_utils'; +import { + clampToLatBounds, + clampToLonBounds, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../../../../plugins/maps/public/elasticsearch_geo_utils'; mapboxgl.workerUrl = mbWorkerUrl; mapboxgl.setRTLTextPlugin(mbRtlPlugin); 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 9ebaee57fba4db..e319535b4a45ca 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 e73a51ffa2ced9..8bad536b39245a 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 161b5b81c12554..8f11d1b23376c9 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/maps/public/connected_components/widget_overlay/view_control/view_control.js b/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/view_control/view_control.js index 445e5be542ffb4..19b60221ead36e 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/view_control/view_control.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/view_control/view_control.js @@ -5,28 +5,33 @@ */ import _ from 'lodash'; -import React from 'react'; +import React, { Fragment } from 'react'; import { EuiText } from '@elastic/eui'; import { DECIMAL_DEGREES_PRECISION } from '../../../../common/constants'; import { FormattedMessage } from '@kbn/i18n/react'; export function ViewControl({ mouseCoordinates, zoom }) { - if (!mouseCoordinates) { - return null; + let latLon; + if (mouseCoordinates) { + latLon = ( + + + + {' '} + {_.round(mouseCoordinates.lat, DECIMAL_DEGREES_PRECISION)},{' '} + + + {' '} + {_.round(mouseCoordinates.lon, DECIMAL_DEGREES_PRECISION)},{' '} + + ); } return (
- - - {' '} - {_.round(mouseCoordinates.lat, DECIMAL_DEGREES_PRECISION)},{' '} - - - {' '} - {_.round(mouseCoordinates.lon, DECIMAL_DEGREES_PRECISION)},{' '} + {latLon} {' '} diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx index 9544e8714f265f..bdd2d863e6920a 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable.tsx @@ -57,7 +57,8 @@ import { } from '../../../../../plugins/maps/public/reducers/non_serializable_instances'; import { getMapCenter, getMapZoom, getHiddenLayerIds } from '../selectors/map_selectors'; import { MAP_SAVED_OBJECT_TYPE } from '../../common/constants'; -import { RenderToolTipContent } from '../layers/tooltips/tooltip_property'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { RenderToolTipContent } from '../../../../../plugins/maps/public/layers/tooltips/tooltip_property'; interface MapEmbeddableConfig { editUrl?: string; diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts index 5a036ed47fb622..5deb3057a449ee 100644 --- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts +++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts @@ -27,6 +27,11 @@ import { getInitialLayers } from '../angular/get_initial_layers'; import { mergeInputWithSavedMap } from './merge_input_with_saved_map'; import '../angular/services/gis_map_saved_object_loader'; import { bindSetupCoreAndPlugins, bindStartCoreAndPlugins } from '../plugin'; +// @ts-ignore +import { + bindSetupCoreAndPlugins as bindNpSetupCoreAndPlugins, + bindStartCoreAndPlugins as bindNpStartCoreAndPlugins, +} from '../../../../../plugins/maps/public/plugin'; // eslint-disable-line @kbn/eslint/no-restricted-paths export class MapEmbeddableFactory implements EmbeddableFactoryDefinition { type = MAP_SAVED_OBJECT_TYPE; @@ -40,7 +45,9 @@ export class MapEmbeddableFactory implements EmbeddableFactoryDefinition { constructor() { // Init required services. Necessary while in legacy bindSetupCoreAndPlugins(npSetup.core, npSetup.plugins); + bindNpSetupCoreAndPlugins(npSetup.core, npSetup.plugins); bindStartCoreAndPlugins(npStart.core, npStart.plugins); + bindNpStartCoreAndPlugins(npStart.core, npStart.plugins); } async isEditable() { diff --git a/x-pack/legacy/plugins/maps/public/index.scss b/x-pack/legacy/plugins/maps/public/index.scss index 328b2e576e0e62..b2ac514299d80c 100644 --- a/x-pack/legacy/plugins/maps/public/index.scss +++ b/x-pack/legacy/plugins/maps/public/index.scss @@ -14,4 +14,4 @@ @import './mapbox_hacks'; @import './connected_components/index'; @import './components/index'; -@import './layers/index'; +@import '../../../../plugins/maps/public/layers/index'; diff --git a/x-pack/legacy/plugins/maps/public/index.ts b/x-pack/legacy/plugins/maps/public/index.ts index 2d13f005f1a70e..b69485e251be45 100644 --- a/x-pack/legacy/plugins/maps/public/index.ts +++ b/x-pack/legacy/plugins/maps/public/index.ts @@ -26,5 +26,9 @@ export const plugin = (initializerContext: PluginInitializerContext) => { return new MapsPlugin(); }; -export { RenderTooltipContentParams, ITooltipProperty } from './layers/tooltips/tooltip_property'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +export { + RenderTooltipContentParams, + ITooltipProperty, +} from '../../../../plugins/maps/public/layers/tooltips/tooltip_property'; export { MapEmbeddable, MapEmbeddableInput } from './embeddable'; diff --git a/x-pack/legacy/plugins/maps/public/kibana_services.js b/x-pack/legacy/plugins/maps/public/kibana_services.js index 3b0f501dc0f602..a6491fe1aa6d42 100644 --- a/x-pack/legacy/plugins/maps/public/kibana_services.js +++ b/x-pack/legacy/plugins/maps/public/kibana_services.js @@ -4,88 +4,26 @@ * you may not use this file except in compliance with the Elastic License. */ -import { esFilters, search } from '../../../../../src/plugins/data/public'; -const { getRequestInspectorStats, getResponseInspectorStats } = search; - -export const SPATIAL_FILTER_TYPE = esFilters.FILTERS.SPATIAL_FILTER; -export { SearchSource } from '../../../../../src/plugins/data/public'; - let indexPatternService; export const setIndexPatternService = dataIndexPatterns => (indexPatternService = dataIndexPatterns); export const getIndexPatternService = () => indexPatternService; -let autocompleteService; -export const setAutocompleteService = dataAutoComplete => (autocompleteService = dataAutoComplete); -export const getAutocompleteService = () => autocompleteService; - -let licenseId; -export const setLicenseId = latestLicenseId => (licenseId = latestLicenseId); -export const getLicenseId = () => { - return licenseId; -}; - let inspector; export const setInspector = newInspector => (inspector = newInspector); export const getInspector = () => { return inspector; }; -let fileUploadPlugin; -export const setFileUpload = fileUpload => (fileUploadPlugin = fileUpload); -export const getFileUploadComponent = () => { - return fileUploadPlugin.JsonUploadAndParse; -}; - let getInjectedVar; export const setInjectedVarFunc = getInjectedVarFunc => (getInjectedVar = getInjectedVarFunc); export const getInjectedVarFunc = () => getInjectedVar; -let uiSettings; -export const setUiSettings = coreUiSettings => (uiSettings = coreUiSettings); -export const getUiSettings = () => uiSettings; - let indexPatternSelectComponent; export const setIndexPatternSelect = indexPatternSelect => (indexPatternSelectComponent = indexPatternSelect); export const getIndexPatternSelectComponent = () => indexPatternSelectComponent; -let coreHttp; -export const setHttp = http => (coreHttp = http); -export const getHttp = () => coreHttp; - let dataTimeFilter; export const setTimeFilter = timeFilter => (dataTimeFilter = timeFilter); export const getTimeFilter = () => dataTimeFilter; - -let toast; -export const setToasts = notificationToast => (toast = notificationToast); -export const getToasts = () => toast; - -export async function fetchSearchSourceAndRecordWithInspector({ - searchSource, - requestId, - requestName, - requestDesc, - inspectorAdapters, - abortSignal, -}) { - const inspectorRequest = inspectorAdapters.requests.start(requestName, { - id: requestId, - description: requestDesc, - }); - let resp; - try { - inspectorRequest.stats(getRequestInspectorStats(searchSource)); - searchSource.getSearchRequestBody().then(body => { - inspectorRequest.json(body); - }); - resp = await searchSource.fetch({ abortSignal }); - inspectorRequest.stats(getResponseInspectorStats(searchSource, resp)).ok({ json: resp }); - } catch (error) { - inspectorRequest.error({ error }); - throw error; - } - - return resp; -} diff --git a/x-pack/legacy/plugins/maps/public/layers/_index.scss b/x-pack/legacy/plugins/maps/public/layers/_index.scss deleted file mode 100644 index a2ce58e0381af1..00000000000000 --- a/x-pack/legacy/plugins/maps/public/layers/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import './styles/index'; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/_index.scss b/x-pack/legacy/plugins/maps/public/layers/styles/_index.scss deleted file mode 100644 index b5d9113619c769..00000000000000 --- a/x-pack/legacy/plugins/maps/public/layers/styles/_index.scss +++ /dev/null @@ -1,4 +0,0 @@ -@import './components/color_gradient'; -@import './vector/components/style_prop_editor'; -@import './vector/components/color/color_stops'; -@import './vector/components/symbol/icon_select'; diff --git a/x-pack/legacy/plugins/maps/public/plugin.ts b/x-pack/legacy/plugins/maps/public/plugin.ts index c08ed6fc6da61c..0fa7e1106a6df1 100644 --- a/x-pack/legacy/plugins/maps/public/plugin.ts +++ b/x-pack/legacy/plugins/maps/public/plugin.ts @@ -4,9 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import './layers/layer_wizard_registry'; -import './layers/sources/source_registry'; -import './layers/load_layer_wizards'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import '../../../../plugins/maps/public/layers/layer_wizard_registry'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import '../../../../plugins/maps/public/layers/sources/source_registry'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import '../../../../plugins/maps/public/layers/load_layer_wizards'; import { Plugin, CoreStart, CoreSetup } from 'src/core/public'; // @ts-ignore @@ -17,20 +20,17 @@ import { Start as InspectorStartContract } from 'src/plugins/inspector/public'; import { MapListing } from './components/map_listing'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { - setLicenseId, setInspector, - setFileUpload, setIndexPatternSelect, - setHttp, setTimeFilter, - setUiSettings, setInjectedVarFunc, - setToasts, setIndexPatternService, - setAutocompleteService, } from './kibana_services'; // @ts-ignore -import { setInjectedVarFunc as npSetInjectedVarFunc } from '../../../../plugins/maps/public/kibana_services'; // eslint-disable-line @kbn/eslint/no-restricted-paths +import { + bindSetupCoreAndPlugins as bindNpSetupCoreAndPlugins, + bindStartCoreAndPlugins as bindNpStartCoreAndPlugins, +} from '../../../../plugins/maps/public/plugin'; // eslint-disable-line @kbn/eslint/no-restricted-paths import { HomePublicPluginSetup } from '../../../../../src/plugins/home/public'; import { LicensingPluginSetup } from '../../../../plugins/licensing/public'; import { featureCatalogueEntry } from './feature_catalogue_entry'; @@ -63,27 +63,17 @@ interface MapsPluginStartDependencies { } export const bindSetupCoreAndPlugins = (core: CoreSetup, plugins: any) => { - const { licensing } = plugins; - const { injectedMetadata, http } = core; - if (licensing) { - licensing.license$.subscribe(({ uid }: { uid: string }) => setLicenseId(uid)); - } + const { injectedMetadata } = core; setInjectedVarFunc(injectedMetadata.getInjectedVar); - setHttp(http); - setUiSettings(core.uiSettings); setInjectedVarFunc(core.injectedMetadata.getInjectedVar); - npSetInjectedVarFunc(core.injectedMetadata.getInjectedVar); - setToasts(core.notifications.toasts); }; export const bindStartCoreAndPlugins = (core: CoreStart, plugins: any) => { - const { file_upload, data, inspector } = plugins; + const { data, inspector } = plugins; setInspector(inspector); - setFileUpload(file_upload); setIndexPatternSelect(data.ui.IndexPatternSelect); setTimeFilter(data.query.timefilter.timefilter); setIndexPatternService(data.indexPatterns); - setAutocompleteService(data.autocomplete); }; /** @internal */ @@ -96,11 +86,13 @@ export class MapsPlugin implements Plugin { }); bindSetupCoreAndPlugins(core, np); + bindNpSetupCoreAndPlugins(core, np); np.home.featureCatalogue.register(featureCatalogueEntry); } public start(core: CoreStart, plugins: MapsPluginStartDependencies) { bindStartCoreAndPlugins(core, plugins); + bindNpStartCoreAndPlugins(core, plugins); } } diff --git a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js index 397478cfd1d1bc..59346e4c6fb989 100644 --- a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js +++ b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js @@ -6,11 +6,16 @@ import { createSelector } from 'reselect'; import _ from 'lodash'; -import { TileLayer } from '../layers/tile_layer'; -import { VectorTileLayer } from '../layers/vector_tile_layer'; -import { VectorLayer } from '../layers/vector_layer'; -import { HeatmapLayer } from '../layers/heatmap_layer'; -import { BlendedVectorLayer } from '../layers/blended_vector_layer'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { TileLayer } from '../../../../../plugins/maps/public/layers/tile_layer'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { VectorTileLayer } from '../../../../../plugins/maps/public/layers/vector_tile_layer'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { VectorLayer } from '../../../../../plugins/maps/public/layers/vector_layer'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { HeatmapLayer } from '../../../../../plugins/maps/public/layers/heatmap_layer'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { BlendedVectorLayer } from '../../../../../plugins/maps/public/layers/blended_vector_layer'; import { getTimeFilter } from '../kibana_services'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { getInspectorAdapters } from '../../../../../plugins/maps/public/reducers/non_serializable_instances'; @@ -19,8 +24,10 @@ import { TRACKED_LAYER_DESCRIPTOR, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../../plugins/maps/public/reducers/util'; -import { InnerJoin } from '../layers/joins/inner_join'; -import { getSourceByType } from '../layers/sources/source_registry'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { InnerJoin } from '../../../../../plugins/maps/public/layers/joins/inner_join'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { getSourceByType } from '../../../../../plugins/maps/public/layers/sources/source_registry'; function createLayerInstance(layerDescriptor, inspectorAdapters) { const source = createSourceInstance(layerDescriptor.sourceDescriptor, inspectorAdapters); diff --git a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.test.js b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.test.js index 1a5ab633a569f9..77bd29259647ce 100644 --- a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.test.js +++ b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.test.js @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -jest.mock('../layers/vector_layer', () => {}); -jest.mock('../layers/blended_vector_layer', () => {}); -jest.mock('../layers/heatmap_layer', () => {}); -jest.mock('../layers/vector_tile_layer', () => {}); -jest.mock('../layers/joins/inner_join', () => {}); +jest.mock('../../../../../plugins/maps/public/layers/vector_layer', () => {}); +jest.mock('../../../../../plugins/maps/public/layers/blended_vector_layer', () => {}); +jest.mock('../../../../../plugins/maps/public/layers/heatmap_layer', () => {}); +jest.mock('../../../../../plugins/maps/public/layers/vector_tile_layer', () => {}); +jest.mock('../../../../../plugins/maps/public/layers/joins/inner_join', () => {}); jest.mock('../../../../../plugins/maps/public/reducers/non_serializable_instances', () => ({ getInspectorAdapters: () => { return {}; diff --git a/x-pack/legacy/plugins/monitoring/common/constants.ts b/x-pack/legacy/plugins/monitoring/common/constants.ts index 9a4030f3eb2147..3a4c7b71dcd03d 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 11fcef73a4b976..95c1af5549198f 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 94d951a94fe29a..cb1081c0c14da6 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 0933cd22db7c9f..eaa474ba177b18 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 650294c29e9a50..19a1a61d00a424 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 258a5b68db372e..d3cf4b463a2cc5 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 072a98b1234521..5f5329bf7fff83 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 8455fb8cf3088c..d87ff98e79be00 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 7c065a78a8af98..62cc985887e9f5 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/reporting/export_types/common/execute_job/decrypt_job_headers.ts b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/decrypt_job_headers.ts index 6f415d7ee5ea93..0436f5d5bc8436 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/execute_job/decrypt_job_headers.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/execute_job/decrypt_job_headers.ts @@ -6,7 +6,7 @@ import { i18n } from '@kbn/i18n'; import { cryptoFactory } from '../../../server/lib/crypto'; -import { CryptoFactory, Logger } from '../../../types'; +import { Logger } from '../../../types'; interface HasEncryptedHeaders { headers?: string; @@ -25,9 +25,16 @@ export const decryptJobHeaders = async < job: JobDocPayloadType; logger: Logger; }): Promise> => { - const crypto: CryptoFactory = cryptoFactory(encryptionKey); try { - const decryptedHeaders: Record = await crypto.decrypt(job.headers); + if (typeof job.headers !== 'string') { + throw new Error( + i18n.translate('xpack.reporting.exportTypes.common.missingJobHeadersErrorMessage', { + defaultMessage: 'Job headers are missing', + }) + ); + } + const crypto = cryptoFactory(encryptionKey); + const decryptedHeaders = (await crypto.decrypt(job.headers)) as Record; return decryptedHeaders; } catch (err) { logger.error(err); diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.ts index d78d8a8a8010d8..3a282eb0b29746 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.ts @@ -43,9 +43,18 @@ export const executeJobFactory: ExecuteJobFactory { - let decryptedHeaders; try { - decryptedHeaders = await crypto.decrypt(headers); + if (typeof headers !== 'string') { + throw new Error( + i18n.translate( + 'xpack.reporting.exportTypes.csv.executeJob.missingJobHeadersErrorMessage', + { + defaultMessage: 'Job headers are missing', + } + ) + ); + } + return await crypto.decrypt(headers); } catch (err) { logger.error(err); throw new Error( @@ -58,7 +67,6 @@ export const executeJobFactory: ExecuteJobFactory; const serializedEncryptedHeaders = job.headers; try { - decryptedHeaders = await crypto.decrypt(serializedEncryptedHeaders); + if (typeof serializedEncryptedHeaders !== 'string') { + throw new Error( + i18n.translate( + 'xpack.reporting.exportTypes.csv_from_savedobject.executeJob.missingJobHeadersErrorMessage', + { + defaultMessage: 'Job headers are missing', + } + ) + ); + } + decryptedHeaders = (await crypto.decrypt(serializedEncryptedHeaders)) as Record< + string, + unknown + >; } catch (err) { jobLogger.error(err); throw new Error( diff --git a/x-pack/legacy/plugins/reporting/server/lib/crypto.ts b/x-pack/legacy/plugins/reporting/server/lib/crypto.ts index 97876529ecfa71..0394c8ed1fbad7 100644 --- a/x-pack/legacy/plugins/reporting/server/lib/crypto.ts +++ b/x-pack/legacy/plugins/reporting/server/lib/crypto.ts @@ -6,6 +6,10 @@ import nodeCrypto from '@elastic/node-crypto'; -export function cryptoFactory(encryptionKey: string | undefined) { +export function cryptoFactory(encryptionKey?: string) { + if (typeof encryptionKey !== 'string') { + throw new Error('Encryption Key required.'); + } + return nodeCrypto({ encryptionKey }); } diff --git a/x-pack/legacy/plugins/reporting/types.d.ts b/x-pack/legacy/plugins/reporting/types.d.ts index 09d53278941c91..7334a859005e0c 100644 --- a/x-pack/legacy/plugins/reporting/types.d.ts +++ b/x-pack/legacy/plugins/reporting/types.d.ts @@ -116,10 +116,6 @@ export interface ConditionalHeadersConditions { basePath: string; } -export interface CryptoFactory { - decrypt: (headers?: string) => any; -} - export interface IndexPatternSavedObject { attributes: { fieldFormatMap: string; diff --git a/x-pack/legacy/plugins/rollup/kibana.json b/x-pack/legacy/plugins/rollup/kibana.json index 3781d59d8c0f36..3df8bd7c187d5e 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 f0eb21a219442f..f4de2a30981276 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 fbf2612b83aa82..809a76d1868b22 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 ec530e63408f45..83945110c2c760 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 c58975419e20f1..5782e88c3448bf 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/cypress/integration/detections_timeline.spec.ts b/x-pack/legacy/plugins/siem/cypress/integration/detections_timeline.spec.ts new file mode 100644 index 00000000000000..2cac6e0f603b91 --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/integration/detections_timeline.spec.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SIGNAL_ID } from '../screens/detections'; +import { PROVIDER_BADGE } from '../screens/timeline'; + +import { + expandFirstSignal, + investigateFirstSignalInTimeline, + waitForSignalsPanelToBeLoaded, +} from '../tasks/detections'; +import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; +import { loginAndWaitForPage } from '../tasks/login'; + +import { DETECTIONS } from '../urls/navigation'; + +describe('Detections timeline', () => { + beforeEach(() => { + esArchiverLoad('timeline_signals'); + loginAndWaitForPage(DETECTIONS); + }); + + afterEach(() => { + esArchiverUnload('timeline_signals'); + }); + + it('Investigate signal in default timeline', () => { + waitForSignalsPanelToBeLoaded(); + expandFirstSignal(); + cy.get(SIGNAL_ID) + .first() + .invoke('text') + .then(eventId => { + investigateFirstSignalInTimeline(); + cy.get(PROVIDER_BADGE) + .invoke('text') + .should('eql', `_id: "${eventId}"`); + }); + }); +}); 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 new file mode 100644 index 00000000000000..2d2db9e70255b2 --- /dev/null +++ b/x-pack/legacy/plugins/siem/cypress/integration/signal_detection_rules.spec.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { + FIFTH_RULE, + FIRST_RULE, + RULE_NAME, + SECOND_RULE, + SEVENTH_RULE, +} from '../screens/signal_detection_rules'; + +import { goToManageSignalDetectionRules } from '../tasks/detections'; +import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; +import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; +import { + activateRule, + sortByActivatedRules, + waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded, + waitForRuleToBeActivated, +} from '../tasks/signal_detection_rules'; + +import { DETECTIONS } from '../urls/navigation'; + +describe('Signal detection rules', () => { + before(() => { + esArchiverLoad('prebuilt_rules_loaded'); + }); + + after(() => { + esArchiverUnload('prebuilt_rules_loaded'); + }); + + it.skip('Sorts by activated rules', () => { + loginAndWaitForPageWithoutDateRange(DETECTIONS); + goToManageSignalDetectionRules(); + waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded(); + cy.get(RULE_NAME) + .eq(FIFTH_RULE) + .invoke('text') + .then(fifthRuleName => { + activateRule(FIFTH_RULE); + waitForRuleToBeActivated(); + cy.get(RULE_NAME) + .eq(SEVENTH_RULE) + .invoke('text') + .then(seventhRuleName => { + activateRule(SEVENTH_RULE); + waitForRuleToBeActivated(); + sortByActivatedRules(); + + cy.get(RULE_NAME) + .eq(FIRST_RULE) + .should('have.text', fifthRuleName); + cy.get(RULE_NAME) + .eq(SECOND_RULE) + .should('have.text', seventhRuleName); + }); + }); + }); +}); diff --git a/x-pack/legacy/plugins/maps/public/layers/tooltips/es_agg_tooltip_property.ts b/x-pack/legacy/plugins/siem/cypress/objects/timeline.ts similarity index 58% rename from x-pack/legacy/plugins/maps/public/layers/tooltips/es_agg_tooltip_property.ts rename to x-pack/legacy/plugins/siem/cypress/objects/timeline.ts index 24011c51ddbaad..bca99bfa9266a4 100644 --- a/x-pack/legacy/plugins/maps/public/layers/tooltips/es_agg_tooltip_property.ts +++ b/x-pack/legacy/plugins/siem/cypress/objects/timeline.ts @@ -3,10 +3,8 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { ESTooltipProperty } from './es_tooltip_property'; -export class ESAggTooltipProperty extends ESTooltipProperty { - isFilterable(): boolean { - return false; - } +interface Timeline { + title: string; + query: string; } diff --git a/x-pack/legacy/plugins/siem/cypress/screens/detections.ts b/x-pack/legacy/plugins/siem/cypress/screens/detections.ts index cb776be8d7b6bb..d9ffa5b5a4ab20 100644 --- a/x-pack/legacy/plugins/siem/cypress/screens/detections.ts +++ b/x-pack/legacy/plugins/siem/cypress/screens/detections.ts @@ -6,6 +6,8 @@ export const CLOSED_SIGNALS_BTN = '[data-test-subj="closedSignals"]'; +export const EXPAND_SIGNAL_BTN = '[data-test-subj="expand-event"]'; + export const LOADING_SIGNALS_PANEL = '[data-test-subj="loading-signals-panel"]'; export const MANAGE_SIGNAL_DETECTION_RULES_BTN = '[data-test-subj="manage-signal-detection-rules"]'; @@ -20,8 +22,12 @@ export const OPENED_SIGNALS_BTN = '[data-test-subj="openSignals"]'; export const SELECTED_SIGNALS = '[data-test-subj="selectedSignals"]'; +export const SEND_SIGNAL_TO_TIMELINE_BTN = '[data-test-subj="send-signal-to-timeline-button"]'; + export const SHOWING_SIGNALS = '[data-test-subj="showingSignals"]'; export const SIGNALS = '[data-test-subj="event"]'; +export const SIGNAL_ID = '[data-test-subj="draggable-content-_id"]'; + export const SIGNAL_CHECKBOX = '[data-test-subj="select-event-container"] .euiCheckbox__input'; diff --git a/x-pack/legacy/plugins/siem/cypress/screens/signal_detection_rules.ts b/x-pack/legacy/plugins/siem/cypress/screens/signal_detection_rules.ts index 09fbc2132302ce..f74f5c26ddc2e4 100644 --- a/x-pack/legacy/plugins/siem/cypress/screens/signal_detection_rules.ts +++ b/x-pack/legacy/plugins/siem/cypress/screens/signal_detection_rules.ts @@ -18,6 +18,10 @@ export const DELETE_RULE_BULK_BTN = '[data-test-subj="deleteRuleBulk"]'; export const ELASTIC_RULES_BTN = '[data-test-subj="show-elastic-rules-filter-button"]'; +export const FIFTH_RULE = 4; + +export const FIRST_RULE = 0; + export const LOAD_PREBUILT_RULES_BTN = '[data-test-subj="load-prebuilt-rules"]'; export const LOADING_INITIAL_PREBUILT_RULES_TABLE = @@ -31,18 +35,26 @@ export const RISK_SCORE = '[data-test-subj="riskScore"]'; export const RELOAD_PREBUILT_RULES_BTN = '[data-test-subj="reloadPrebuiltRulesBtn"]'; +export const SECOND_RULE = 1; + export const RULE_CHECKBOX = '.euiTableRow .euiCheckbox__input'; export const RULE_NAME = '[data-test-subj="ruleName"]'; export const RULE_SWITCH = '[data-test-subj="rule-switch"]'; +export const RULE_SWITCH_LOADER = '[data-test-subj="rule-switch-loader"]'; + export const RULES_TABLE = '[data-test-subj="rules-table"]'; export const RULES_ROW = '.euiTableRow'; +export const SEVENTH_RULE = 6; + export const SEVERITY = '[data-test-subj="severity"]'; export const SHOWING_RULES_TEXT = '[data-test-subj="showingRules"]'; +export const SORT_RULES_BTN = '[data-test-subj="tableHeaderSortButton"]'; + export const THREE_HUNDRED_ROWS = '[data-test-subj="tablePagination-300-rows"]'; diff --git a/x-pack/legacy/plugins/siem/cypress/screens/timeline.ts b/x-pack/legacy/plugins/siem/cypress/screens/timeline.ts index fbce585a70f866..53d8273d9ce6b6 100644 --- a/x-pack/legacy/plugins/siem/cypress/screens/timeline.ts +++ b/x-pack/legacy/plugins/siem/cypress/screens/timeline.ts @@ -14,6 +14,8 @@ export const ID_FIELD = '[data-test-subj="timeline"] [data-test-subj="field-name export const ID_TOGGLE_FIELD = '[data-test-subj="toggle-field-_id"]'; +export const PROVIDER_BADGE = '[data-test-subj="providerBadge"]'; + export const SEARCH_OR_FILTER_CONTAINER = '[data-test-subj="timeline-search-or-filter-search-container"]'; diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts b/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts index abea4a887b8ba9..c30a178eab489d 100644 --- a/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts +++ b/x-pack/legacy/plugins/siem/cypress/tasks/detections.ts @@ -6,11 +6,13 @@ import { CLOSED_SIGNALS_BTN, + EXPAND_SIGNAL_BTN, LOADING_SIGNALS_PANEL, MANAGE_SIGNAL_DETECTION_RULES_BTN, OPEN_CLOSE_SIGNAL_BTN, OPEN_CLOSE_SIGNALS_BTN, OPENED_SIGNALS_BTN, + SEND_SIGNAL_TO_TIMELINE_BTN, SIGNALS, SIGNAL_CHECKBOX, } from '../screens/detections'; @@ -26,6 +28,12 @@ export const closeSignals = () => { cy.get(OPEN_CLOSE_SIGNALS_BTN).click({ force: true }); }; +export const expandFirstSignal = () => { + cy.get(EXPAND_SIGNAL_BTN) + .first() + .click({ force: true }); +}; + export const goToClosedSignals = () => { cy.get(CLOSED_SIGNALS_BTN).click({ force: true }); }; @@ -58,6 +66,12 @@ export const selectNumberOfSignals = (numberOfSignals: number) => { } }; +export const investigateFirstSignalInTimeline = () => { + cy.get(SEND_SIGNAL_TO_TIMELINE_BTN) + .first() + .click({ force: true }); +}; + export const waitForSignals = () => { cy.get(REFRESH_BUTTON) .invoke('text') diff --git a/x-pack/legacy/plugins/siem/cypress/tasks/signal_detection_rules.ts b/x-pack/legacy/plugins/siem/cypress/tasks/signal_detection_rules.ts index cfc490526d84e3..a404f1142cba7b 100644 --- a/x-pack/legacy/plugins/siem/cypress/tasks/signal_detection_rules.ts +++ b/x-pack/legacy/plugins/siem/cypress/tasks/signal_detection_rules.ts @@ -15,13 +15,22 @@ import { LOADING_INITIAL_PREBUILT_RULES_TABLE, LOADING_SPINNER, PAGINATION_POPOVER_BTN, + RELOAD_PREBUILT_RULES_BTN, RULE_CHECKBOX, RULE_NAME, + RULE_SWITCH, + RULE_SWITCH_LOADER, RULES_TABLE, + SORT_RULES_BTN, THREE_HUNDRED_ROWS, - RELOAD_PREBUILT_RULES_BTN, } from '../screens/signal_detection_rules'; +export const activateRule = (rulePosition: number) => { + cy.get(RULE_SWITCH) + .eq(rulePosition) + .click({ force: true }); +}; + export const changeToThreeHundredRowsPerPage = () => { cy.get(PAGINATION_POPOVER_BTN).click({ force: true }); cy.get(THREE_HUNDRED_ROWS).click(); @@ -71,6 +80,13 @@ export const selectNumberOfRules = (numberOfRules: number) => { } }; +export const sortByActivatedRules = () => { + cy.get(SORT_RULES_BTN).click({ force: true }); + waitForRulesToBeLoaded(); + cy.get(SORT_RULES_BTN).click({ force: true }); + waitForRulesToBeLoaded(); +}; + export const waitForLoadElasticPrebuiltDetectionRulesTableToBeLoaded = () => { cy.get(LOADING_INITIAL_PREBUILT_RULES_TABLE).should('exist'); cy.get(LOADING_INITIAL_PREBUILT_RULES_TABLE).should('not.exist'); @@ -81,6 +97,11 @@ export const waitForPrebuiltDetectionRulesToBeLoaded = () => { cy.get(RULES_TABLE).should('exist'); }; +export const waitForRuleToBeActivated = () => { + cy.get(RULE_SWITCH_LOADER).should('exist'); + cy.get(RULE_SWITCH_LOADER).should('not.exist'); +}; + export const waitForRulesToBeLoaded = () => { cy.get(LOADING_SPINNER).should('exist'); cy.get(LOADING_SPINNER).should('not.exist'); diff --git a/x-pack/legacy/plugins/siem/public/components/header_global/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/header_global/index.test.tsx index 098de39bbfef53..56fa0d56f3c3a1 100644 --- a/x-pack/legacy/plugins/siem/public/components/header_global/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/header_global/index.test.tsx @@ -10,6 +10,16 @@ import React from 'react'; import '../../mock/match_media'; import { HeaderGlobal } from './index'; +jest.mock('react-router-dom', () => ({ + useLocation: () => ({ + pathname: '/app/siem#/hosts/allHosts', + hash: '', + search: '', + state: '', + }), + withRouter: () => jest.fn(), +})); + jest.mock('ui/new_platform'); // Test will fail because we will to need to mock some core services to make the test work @@ -19,6 +29,10 @@ jest.mock('../search_bar', () => ({ })); describe('HeaderGlobal', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + test('it renders', () => { const wrapper = shallow(); diff --git a/x-pack/legacy/plugins/siem/public/components/header_global/index.tsx b/x-pack/legacy/plugins/siem/public/components/header_global/index.tsx index a12fab8a4f5d92..adc2be4f9c3656 100644 --- a/x-pack/legacy/plugins/siem/public/components/header_global/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/header_global/index.tsx @@ -9,6 +9,7 @@ import { pickBy } from 'lodash/fp'; import React from 'react'; import styled, { css } from 'styled-components'; +import { useLocation } from 'react-router-dom'; import { gutterTimeline } from '../../lib/helpers'; import { navTabs } from '../../pages/home/home_navigations'; import { SiemPageName } from '../../pages/home/types'; @@ -36,63 +37,68 @@ FlexItem.displayName = 'FlexItem'; interface HeaderGlobalProps { hideDetectionEngine?: boolean; } -export const HeaderGlobal = React.memo(({ hideDetectionEngine = false }) => ( - - - - {({ indicesExist }) => ( - <> - - - - - - - +export const HeaderGlobal = React.memo(({ hideDetectionEngine = false }) => { + const currentLocation = useLocation(); - - {indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( - key !== SiemPageName.detections, navTabs) - : navTabs - } - /> - ) : ( - key === SiemPageName.overview, navTabs)} - /> - )} - - - - - - - {indicesExistOrDataTemporarilyUnavailable(indicesExist) && ( + return ( + + + + {({ indicesExist }) => ( + <> + + - + + + + + + + {indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( + key !== SiemPageName.detections, navTabs) + : navTabs + } + /> + ) : ( + key === SiemPageName.overview, navTabs)} + /> + )} - )} + + - - - {i18n.BUTTON_ADD_DATA} - - - - - - )} - - - -)); + + + {indicesExistOrDataTemporarilyUnavailable(indicesExist) && + currentLocation.pathname.includes(`/${SiemPageName.detections}/`) && ( + + + + )} + + + + {i18n.BUTTON_ADD_DATA} + + + + + + )} + + + + ); +}); HeaderGlobal.displayName = 'HeaderGlobal'; diff --git a/x-pack/legacy/plugins/siem/public/components/ml_popover/__snapshots__/popover_description.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/ml_popover/__snapshots__/popover_description.test.tsx.snap index 09e95c5ff59eae..46e61f9e939ee6 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml_popover/__snapshots__/popover_description.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/ml_popover/__snapshots__/popover_description.test.tsx.snap @@ -5,7 +5,7 @@ exports[`JobsTableFilters renders correctly against snapshot 1`] = ` size="s" > { iconSide="right" onClick={() => setIsPopoverOpen(!isPopoverOpen)} > - {i18n.ANOMALY_DETECTION} + {i18n.ML_JOB_SETTINGS} } isOpen={isPopoverOpen} @@ -142,14 +142,14 @@ export const MlPopover = React.memo(() => { dispatch({ type: 'refresh' }); }} > - {i18n.ANOMALY_DETECTION} + {i18n.ML_JOB_SETTINGS} } isOpen={isPopoverOpen} closePopover={() => setIsPopoverOpen(!isPopoverOpen)} > - {i18n.ANOMALY_DETECTION_TITLE} + {i18n.ML_JOB_SETTINGS} diff --git a/x-pack/legacy/plugins/siem/public/components/ml_popover/popover_description.tsx b/x-pack/legacy/plugins/siem/public/components/ml_popover/popover_description.tsx index 20e8dd2492fef3..a491d4b6b769c8 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml_popover/popover_description.tsx +++ b/x-pack/legacy/plugins/siem/public/components/ml_popover/popover_description.tsx @@ -14,7 +14,7 @@ export const PopoverDescriptionComponent = () => ( diff --git a/x-pack/legacy/plugins/siem/public/components/ml_popover/translations.ts b/x-pack/legacy/plugins/siem/public/components/ml_popover/translations.ts index 442068dd0e193e..613691e55dcfd7 100644 --- a/x-pack/legacy/plugins/siem/public/components/ml_popover/translations.ts +++ b/x-pack/legacy/plugins/siem/public/components/ml_popover/translations.ts @@ -6,17 +6,10 @@ import { i18n } from '@kbn/i18n'; -export const ANOMALY_DETECTION = i18n.translate( - 'xpack.siem.components.mlPopup.anomalyDetectionButtonLabel', +export const ML_JOB_SETTINGS = i18n.translate( + 'xpack.siem.components.mlPopup.mlJobSettingsButtonLabel', { - defaultMessage: 'Anomaly detection', - } -); - -export const ANOMALY_DETECTION_TITLE = i18n.translate( - 'xpack.siem.components.mlPopup.anomalyDetectionTitle', - { - defaultMessage: 'Anomaly detection settings', + defaultMessage: 'ML job settings', } ); diff --git a/x-pack/legacy/plugins/siem/public/components/toasters/utils.ts b/x-pack/legacy/plugins/siem/public/components/toasters/utils.ts index e624144c9826f6..e7cc389d4c06bd 100644 --- a/x-pack/legacy/plugins/siem/public/components/toasters/utils.ts +++ b/x-pack/legacy/plugins/siem/public/components/toasters/utils.ts @@ -37,6 +37,30 @@ export const displayErrorToast = ( }); }; +/** + * Displays a warning toast for the provided title and message + * + * @param title warning message to display in toaster and modal + * @param dispatchToaster provided by useStateToaster() + * @param id unique ID if necessary + */ +export const displayWarningToast = ( + title: string, + dispatchToaster: React.Dispatch, + id: string = uuid.v4() +): void => { + const toast: AppToast = { + id, + title, + color: 'warning', + iconType: 'help', + }; + dispatchToaster({ + type: 'addToaster', + toast, + }); +}; + /** * Displays a success toast for the provided title and message * 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 ed47cdc62a1b67..c24081c777a968 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 d31dcdbee2a146..30108ecf33874a 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 bc559c5ac49721..f89d21ef1aeb19 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 new file mode 100644 index 00000000000000..135f0f2a7e26d7 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/__mock__/index.tsx @@ -0,0 +1,124 @@ +/* + * 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 { + Connector, + CasesConfigurationMapping, +} from '../../../../../containers/case/configure/types'; +import { State } from '../reducer'; +import { ReturnConnectors } from '../../../../../containers/case/configure/use_connectors'; +import { ReturnUseCaseConfigure } from '../../../../../containers/case/configure/use_configure'; +import { createUseKibanaMock } from '../../../../../mock/kibana_react'; + +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { actionTypeRegistryMock } from '../../../../../../../../../plugins/triggers_actions_ui/public/application/action_type_registry.mock'; + +export const connectors: Connector[] = [ + { + id: '123', + actionTypeId: '.servicenow', + name: 'My Connector', + isPreconfigured: false, + config: { + apiUrl: 'https://instance1.service-now.com', + casesConfiguration: { + mapping: [ + { + source: 'title', + target: 'short_description', + actionType: 'overwrite', + }, + { + source: 'description', + target: 'description', + actionType: 'append', + }, + { + source: 'comments', + target: 'comments', + actionType: 'append', + }, + ], + }, + }, + }, + { + id: '456', + actionTypeId: '.servicenow', + name: 'My Connector 2', + isPreconfigured: false, + config: { + apiUrl: 'https://instance2.service-now.com', + casesConfiguration: { + mapping: [ + { + source: 'title', + target: 'short_description', + actionType: 'overwrite', + }, + { + source: 'description', + target: 'description', + actionType: 'overwrite', + }, + { + source: 'comments', + target: 'comments', + actionType: 'append', + }, + ], + }, + }, + }, +]; + +export const mapping: CasesConfigurationMapping[] = [ + { + source: 'title', + target: 'short_description', + actionType: 'overwrite', + }, + { + source: 'description', + target: 'description', + actionType: 'append', + }, + { + source: 'comments', + target: 'comments', + actionType: 'append', + }, +]; + +export const searchURL = + '?timerange=(global:(linkTo:!(),timerange:(from:1585487656371,fromStr:now-24h,kind:relative,to:1585574056371,toStr:now)),timeline:(linkTo:!(),timerange:(from:1585227005527,kind:absolute,to:1585313405527)))'; + +export const initialState: State = { + connectorId: 'none', + closureType: 'close-by-user', + mapping: null, + currentConfiguration: { connectorId: 'none', closureType: 'close-by-user' }, +}; + +export const useCaseConfigureResponse: ReturnUseCaseConfigure = { + loading: false, + persistLoading: false, + refetchCaseConfigure: jest.fn(), + persistCaseConfigure: jest.fn(), +}; + +export const useConnectorsResponse: ReturnConnectors = { + loading: false, + connectors, + refetchConnectors: jest.fn(), +}; + +export const kibanaMockImplementationArgs = { + services: { + ...createUseKibanaMock()().services, + triggers_actions_ui: { actionTypeRegistry: actionTypeRegistryMock.create() }, + }, +}; diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/button.test.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/button.test.tsx new file mode 100644 index 00000000000000..cf52fef94ed17e --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/button.test.tsx @@ -0,0 +1,114 @@ +/* + * 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 { ReactWrapper, mount } from 'enzyme'; +import { EuiText } from '@elastic/eui'; + +import { ConfigureCaseButton, ConfigureCaseButtonProps } from './button'; +import { TestProviders } from '../../../../mock'; +import { searchURL } from './__mock__'; + +describe('Configuration button', () => { + let wrapper: ReactWrapper; + const props: ConfigureCaseButtonProps = { + isDisabled: false, + label: 'My label', + msgTooltip: <>, + showToolTip: false, + titleTooltip: '', + urlSearch: searchURL, + }; + + beforeAll(() => { + wrapper = mount(, { wrappingComponent: TestProviders }); + }); + + test('it renders without the tooltip', () => { + expect( + wrapper + .find('[data-test-subj="configure-case-button"]') + .first() + .exists() + ).toBe(true); + + expect( + wrapper + .find('[data-test-subj="configure-case-tooltip"]') + .first() + .exists() + ).toBe(false); + }); + + test('it pass the correct props to the button', () => { + expect( + wrapper + .find('[data-test-subj="configure-case-button"]') + .first() + .props() + ).toMatchObject({ + href: `#/link-to/case/configure${searchURL}`, + iconType: 'controlsHorizontal', + isDisabled: false, + 'aria-label': 'My label', + children: 'My label', + }); + }); + + test('it renders the tooltip', () => { + const msgTooltip = {'My message tooltip'}; + + const newWrapper = mount( + , + { + wrappingComponent: TestProviders, + } + ); + + expect( + newWrapper + .find('[data-test-subj="configure-case-tooltip"]') + .first() + .exists() + ).toBe(true); + + expect( + wrapper + .find('[data-test-subj="configure-case-button"]') + .first() + .exists() + ).toBe(true); + }); + + test('it shows the tooltip when hovering the button', () => { + const msgTooltip = 'My message tooltip'; + const titleTooltip = 'My title'; + + const newWrapper = mount( + {msgTooltip}} + />, + { + wrappingComponent: TestProviders, + } + ); + + newWrapper + .find('[data-test-subj="configure-case-button"]') + .first() + .simulate('mouseOver'); + + expect(newWrapper.find('.euiToolTipPopover').text()).toBe(`${titleTooltip}${msgTooltip}`); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/button.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/button.tsx index b0bea83148bda2..844ffea28415f6 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/button.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/button.tsx @@ -8,7 +8,7 @@ import { EuiButton, EuiToolTip } from '@elastic/eui'; import React, { memo, useMemo } from 'react'; import { getConfigureCasesUrl } from '../../../../components/link_to'; -interface ConfigureCaseButtonProps { +export interface ConfigureCaseButtonProps { label: string; isDisabled: boolean; msgTooltip: JSX.Element; @@ -32,6 +32,7 @@ const ConfigureCaseButtonComponent: React.FC = ({ iconType="controlsHorizontal" isDisabled={isDisabled} aria-label={label} + data-test-subj="configure-case-button" > {label} @@ -39,7 +40,12 @@ const ConfigureCaseButtonComponent: React.FC = ({ [label, isDisabled, urlSearch] ); return showToolTip ? ( - {msgTooltip}

}> + {msgTooltip}

} + data-test-subj="configure-case-tooltip" + > {configureCaseButton}
) : ( diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/closure_options.test.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/closure_options.test.tsx new file mode 100644 index 00000000000000..209dce9aedffc5 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/closure_options.test.tsx @@ -0,0 +1,67 @@ +/* + * 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, ReactWrapper } from 'enzyme'; + +import { ClosureOptions, ClosureOptionsProps } from './closure_options'; +import { TestProviders } from '../../../../mock'; +import { ClosureOptionsRadio } from './closure_options_radio'; + +describe('ClosureOptions', () => { + let wrapper: ReactWrapper; + const onChangeClosureType = jest.fn(); + const props: ClosureOptionsProps = { + disabled: false, + closureTypeSelected: 'close-by-user', + onChangeClosureType, + }; + + beforeAll(() => { + wrapper = mount(, { wrappingComponent: TestProviders }); + }); + + test('it shows the closure options form group', () => { + expect( + wrapper + .find('[data-test-subj="case-closure-options-form-group"]') + .first() + .exists() + ).toBe(true); + }); + + test('it shows the closure options form row', () => { + expect( + wrapper + .find('[data-test-subj="case-closure-options-form-row"]') + .first() + .exists() + ).toBe(true); + }); + + test('it shows closure options', () => { + expect( + wrapper + .find('[data-test-subj="case-closure-options-radio"]') + .first() + .exists() + ).toBe(true); + }); + + test('it pass the correct props to child', () => { + const closureOptionsRadioComponent = wrapper.find(ClosureOptionsRadio); + expect(closureOptionsRadioComponent.props().disabled).toEqual(false); + expect(closureOptionsRadioComponent.props().closureTypeSelected).toEqual('close-by-user'); + expect(closureOptionsRadioComponent.props().onChangeClosureType).toEqual(onChangeClosureType); + }); + + test('the closure type is changed successfully', () => { + wrapper.find('input[id="close-by-pushing"]').simulate('change'); + + expect(onChangeClosureType).toHaveBeenCalled(); + expect(onChangeClosureType).toHaveBeenCalledWith('close-by-pushing'); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/closure_options.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/closure_options.tsx index 9879b9149059ae..6fa97818dd0ce3 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/closure_options.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/closure_options.tsx @@ -11,7 +11,7 @@ import { ClosureType } from '../../../../containers/case/configure/types'; import { ClosureOptionsRadio } from './closure_options_radio'; import * as i18n from './translations'; -interface ClosureOptionsProps { +export interface ClosureOptionsProps { closureTypeSelected: ClosureType; disabled: boolean; onChangeClosureType: (newClosureType: ClosureType) => void; @@ -27,12 +27,18 @@ const ClosureOptionsComponent: React.FC = ({ fullWidth title={

{i18n.CASE_CLOSURE_OPTIONS_TITLE}

} description={i18n.CASE_CLOSURE_OPTIONS_DESC} + data-test-subj="case-closure-options-form-group" > - + diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/closure_options_radio.test.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/closure_options_radio.test.tsx new file mode 100644 index 00000000000000..f2ef2c2d55c288 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/closure_options_radio.test.tsx @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { ReactWrapper, mount } from 'enzyme'; + +import { ClosureOptionsRadio, ClosureOptionsRadioComponentProps } from './closure_options_radio'; +import { TestProviders } from '../../../../mock'; + +describe('ClosureOptionsRadio', () => { + let wrapper: ReactWrapper; + const onChangeClosureType = jest.fn(); + const props: ClosureOptionsRadioComponentProps = { + disabled: false, + closureTypeSelected: 'close-by-user', + onChangeClosureType, + }; + + beforeAll(() => { + wrapper = mount(, { wrappingComponent: TestProviders }); + }); + + test('it renders', () => { + expect( + wrapper + .find('[data-test-subj="closure-options-radio-group"]') + .first() + .exists() + ).toBe(true); + }); + + test('it shows the correct number of radio buttons', () => { + expect(wrapper.find('input[name="closure_options"]')).toHaveLength(2); + }); + + test('it renders close by user radio button', () => { + expect(wrapper.find('input[id="close-by-user"]').exists()).toBeTruthy(); + }); + + test('it renders close by pushing radio button', () => { + expect(wrapper.find('input[id="close-by-pushing"]').exists()).toBeTruthy(); + }); + + test('it disables the close by user radio button', () => { + const newWrapper = mount(, { + wrappingComponent: TestProviders, + }); + + expect(newWrapper.find('input[id="close-by-user"]').prop('disabled')).toEqual(true); + }); + + test('it disables correctly the close by pushing radio button', () => { + const newWrapper = mount(, { + wrappingComponent: TestProviders, + }); + + expect(newWrapper.find('input[id="close-by-pushing"]').prop('disabled')).toEqual(true); + }); + + test('it selects the correct radio button', () => { + const newWrapper = mount( + , + { + wrappingComponent: TestProviders, + } + ); + expect(newWrapper.find('input[id="close-by-pushing"]').prop('checked')).toEqual(true); + }); + + test('it calls the onChangeClosureType function', () => { + wrapper.find('input[id="close-by-pushing"]').simulate('change'); + wrapper.update(); + expect(onChangeClosureType).toHaveBeenCalled(); + expect(onChangeClosureType).toHaveBeenCalledWith('close-by-pushing'); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/closure_options_radio.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/closure_options_radio.tsx index f32f867b2471d0..d2cdb7ecda7ba4 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/closure_options_radio.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/closure_options_radio.tsx @@ -26,7 +26,7 @@ const radios: ClosureRadios[] = [ }, ]; -interface ClosureOptionsRadioComponentProps { +export interface ClosureOptionsRadioComponentProps { closureTypeSelected: ClosureType; disabled: boolean; onChangeClosureType: (newClosureType: ClosureType) => void; @@ -51,6 +51,7 @@ const ClosureOptionsRadioComponent: React.FC idSelected={closureTypeSelected} onChange={onChangeLocal} name="closure_options" + data-test-subj="closure-options-radio-group" /> ); }; diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/connectors.test.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/connectors.test.tsx new file mode 100644 index 00000000000000..5fb52c374b482b --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/connectors.test.tsx @@ -0,0 +1,90 @@ +/* + * 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, ReactWrapper } from 'enzyme'; + +import { Connectors, Props } from './connectors'; +import { TestProviders } from '../../../../mock'; +import { ConnectorsDropdown } from './connectors_dropdown'; +import { connectors } from './__mock__'; + +describe('Connectors', () => { + let wrapper: ReactWrapper; + const onChangeConnector = jest.fn(); + const handleShowAddFlyout = jest.fn(); + const props: Props = { + disabled: false, + connectors, + selectedConnector: 'none', + isLoading: false, + onChangeConnector, + handleShowAddFlyout, + }; + + beforeAll(() => { + wrapper = mount(, { wrappingComponent: TestProviders }); + }); + + test('it shows the connectors from group', () => { + expect( + wrapper + .find('[data-test-subj="case-connectors-form-group"]') + .first() + .exists() + ).toBe(true); + }); + + test('it shows the connectors form row', () => { + expect( + wrapper + .find('[data-test-subj="case-connectors-form-row"]') + .first() + .exists() + ).toBe(true); + }); + + test('it shows the connectors dropdown', () => { + expect( + wrapper + .find('[data-test-subj="case-connectors-dropdown"]') + .first() + .exists() + ).toBe(true); + }); + + test('it pass the correct props to child', () => { + const connectorsDropdownProps = wrapper.find(ConnectorsDropdown).props(); + expect(connectorsDropdownProps).toMatchObject({ + disabled: false, + isLoading: false, + connectors, + selectedConnector: 'none', + onChange: props.onChangeConnector, + }); + }); + + test('the connector is changed successfully', () => { + wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); + wrapper.find('button[data-test-subj="dropdown-connector-456"]').simulate('click'); + + expect(onChangeConnector).toHaveBeenCalled(); + expect(onChangeConnector).toHaveBeenCalledWith('456'); + }); + + test('the connector is changed successfully to none', () => { + onChangeConnector.mockClear(); + const newWrapper = mount(, { + wrappingComponent: TestProviders, + }); + + newWrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); + newWrapper.find('button[data-test-subj="dropdown-connector-no-connector"]').simulate('click'); + + expect(onChangeConnector).toHaveBeenCalled(); + expect(onChangeConnector).toHaveBeenCalledWith('none'); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/connectors.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/connectors.tsx index 8fb1cfb1aa6cc4..de6d5f76cfad07 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/connectors.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/connectors.tsx @@ -28,7 +28,7 @@ const EuiFormRowExtended = styled(EuiFormRow)` } `; -interface Props { +export interface Props { connectors: Connector[]; disabled: boolean; isLoading: boolean; @@ -48,7 +48,11 @@ const ConnectorsComponent: React.FC = ({ {i18n.INCIDENT_MANAGEMENT_SYSTEM_LABEL} - + {i18n.ADD_NEW_CONNECTOR} @@ -61,14 +65,20 @@ const ConnectorsComponent: React.FC = ({ fullWidth title={

{i18n.INCIDENT_MANAGEMENT_SYSTEM_TITLE}

} description={i18n.INCIDENT_MANAGEMENT_SYSTEM_DESC} + data-test-subj="case-connectors-form-group" > - + diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/connectors_dropdown.test.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/connectors_dropdown.test.tsx new file mode 100644 index 00000000000000..044108962efc7b --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/connectors_dropdown.test.tsx @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { mount, ReactWrapper } from 'enzyme'; +import { EuiSuperSelect } from '@elastic/eui'; + +import { ConnectorsDropdown, Props } from './connectors_dropdown'; +import { TestProviders } from '../../../../mock'; +import { connectors } from './__mock__'; + +describe('ConnectorsDropdown', () => { + let wrapper: ReactWrapper; + const props: Props = { + disabled: false, + connectors, + isLoading: false, + onChange: jest.fn(), + selectedConnector: 'none', + }; + + beforeAll(() => { + wrapper = mount(, { wrappingComponent: TestProviders }); + }); + + test('it renders', () => { + expect( + wrapper + .find('[data-test-subj="dropdown-connectors"]') + .first() + .exists() + ).toBe(true); + }); + + test('it formats the connectors correctly', () => { + const selectProps = wrapper.find(EuiSuperSelect).props(); + + expect(selectProps.options).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + value: 'none', + 'data-test-subj': 'dropdown-connector-no-connector', + }), + expect.objectContaining({ value: '123', 'data-test-subj': 'dropdown-connector-123' }), + expect.objectContaining({ value: '456', 'data-test-subj': 'dropdown-connector-456' }), + ]) + ); + }); + + test('it disables the dropdown', () => { + const newWrapper = mount(, { + wrappingComponent: TestProviders, + }); + + expect( + newWrapper + .find('[data-test-subj="dropdown-connectors"]') + .first() + .prop('disabled') + ).toEqual(true); + }); + + test('it loading correctly', () => { + const newWrapper = mount(, { + wrappingComponent: TestProviders, + }); + + expect( + newWrapper + .find('[data-test-subj="dropdown-connectors"]') + .first() + .prop('isLoading') + ).toEqual(true); + }); + + test('it selects the correct connector', () => { + const newWrapper = mount(, { + wrappingComponent: TestProviders, + }); + + expect(newWrapper.find('button span').text()).toEqual('My Connector'); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/connectors_dropdown.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/connectors_dropdown.tsx index a0a0ad6cd3e7ff..15066e73eee829 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/connectors_dropdown.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/connectors_dropdown.tsx @@ -12,7 +12,7 @@ import { Connector } from '../../../../containers/case/configure/types'; import { connectors as connectorsDefinition } from '../../../../lib/connectors/config'; import * as i18n from './translations'; -interface Props { +export interface Props { connectors: Connector[]; disabled: boolean; isLoading: boolean; @@ -34,7 +34,7 @@ const noConnectorOption = { {i18n.NO_CONNECTOR} ), - 'data-test-subj': 'no-connector', + 'data-test-subj': 'dropdown-connector-no-connector', }; const ConnectorsDropdownComponent: React.FC = ({ @@ -60,7 +60,7 @@ const ConnectorsDropdownComponent: React.FC = ({ {connector.name} ), - 'data-test-subj': connector.id, + 'data-test-subj': `dropdown-connector-${connector.id}`, }, ], [noConnectorOption] @@ -76,6 +76,7 @@ const ConnectorsDropdownComponent: React.FC = ({ valueOfSelected={selectedConnector} fullWidth onChange={onChange} + data-test-subj="dropdown-connectors" /> ); }; diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/field_mapping.test.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/field_mapping.test.tsx new file mode 100644 index 00000000000000..9ab752bb589c0a --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/field_mapping.test.tsx @@ -0,0 +1,84 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { mount, ReactWrapper } from 'enzyme'; + +import { FieldMapping, FieldMappingProps } from './field_mapping'; +import { mapping } from './__mock__'; +import { FieldMappingRow } from './field_mapping_row'; +import { defaultMapping } from '../../../../lib/connectors/config'; +import { TestProviders } from '../../../../mock'; + +describe('FieldMappingRow', () => { + let wrapper: ReactWrapper; + const onChangeMapping = jest.fn(); + const props: FieldMappingProps = { + disabled: false, + mapping, + onChangeMapping, + }; + + beforeAll(() => { + wrapper = mount(, { wrappingComponent: TestProviders }); + }); + + test('it renders', () => { + expect( + wrapper + .find('[data-test-subj="case-configure-field-mapping-cols"]') + .first() + .exists() + ).toBe(true); + + expect( + wrapper + .find('[data-test-subj="case-configure-field-mapping-row-wrapper"]') + .first() + .exists() + ).toBe(true); + + expect(wrapper.find(FieldMappingRow).length).toEqual(3); + }); + + test('it shows the correct number of FieldMappingRow with default mapping', () => { + const newWrapper = mount(, { + wrappingComponent: TestProviders, + }); + + expect(newWrapper.find(FieldMappingRow).length).toEqual(3); + }); + + test('it pass the corrects props to mapping row', () => { + const rows = wrapper.find(FieldMappingRow); + rows.forEach((row, index) => { + expect(row.prop('siemField')).toEqual(mapping[index].source); + expect(row.prop('selectedActionType')).toEqual(mapping[index].actionType); + expect(row.prop('selectedThirdParty')).toEqual(mapping[index].target); + }); + }); + + test('it pass the default mapping when mapping is null', () => { + const newWrapper = mount(, { + wrappingComponent: TestProviders, + }); + + const rows = newWrapper.find(FieldMappingRow); + rows.forEach((row, index) => { + expect(row.prop('siemField')).toEqual(defaultMapping[index].source); + expect(row.prop('selectedActionType')).toEqual(defaultMapping[index].actionType); + expect(row.prop('selectedThirdParty')).toEqual(defaultMapping[index].target); + }); + }); + + test('it should show zero rows on empty array', () => { + const newWrapper = mount(, { + wrappingComponent: TestProviders, + }); + + expect(newWrapper.find(FieldMappingRow).length).toEqual(0); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/field_mapping.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/field_mapping.tsx index 0c0dc14f1c218d..2934b1056e29cd 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/field_mapping.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/field_mapping.tsx @@ -18,6 +18,7 @@ import { FieldMappingRow } from './field_mapping_row'; import * as i18n from './translations'; import { defaultMapping } from '../../../../lib/connectors/config'; +import { setActionTypeToMapping, setThirdPartyToMapping } from './utils'; const FieldRowWrapper = styled.div` margin-top: 8px; @@ -28,22 +29,26 @@ const supportedThirdPartyFields: Array> = { value: 'not_mapped', inputDisplay: {i18n.FIELD_MAPPING_FIELD_NOT_MAPPED}, + 'data-test-subj': 'third-party-field-not-mapped', }, { value: 'short_description', inputDisplay: {i18n.FIELD_MAPPING_FIELD_SHORT_DESC}, + 'data-test-subj': 'third-party-field-short-description', }, { value: 'comments', inputDisplay: {i18n.FIELD_MAPPING_FIELD_COMMENTS}, + 'data-test-subj': 'third-party-field-comments', }, { value: 'description', inputDisplay: {i18n.FIELD_MAPPING_FIELD_DESC}, + 'data-test-subj': 'third-party-field-description', }, ]; -interface FieldMappingProps { +export interface FieldMappingProps { disabled: boolean; mapping: CasesConfigurationMapping[] | null; onChangeMapping: (newMapping: CasesConfigurationMapping[]) => void; @@ -57,14 +62,7 @@ const FieldMappingComponent: React.FC = ({ const onChangeActionType = useCallback( (caseField: CaseField, newActionType: ActionType) => { const myMapping = mapping ?? defaultMapping; - const findItemIndex = myMapping.findIndex(item => item.source === caseField); - if (findItemIndex >= 0) { - onChangeMapping([ - ...myMapping.slice(0, findItemIndex), - { ...myMapping[findItemIndex], actionType: newActionType }, - ...myMapping.slice(findItemIndex + 1), - ]); - } + onChangeMapping(setActionTypeToMapping(caseField, newActionType, myMapping)); }, [mapping] ); @@ -72,22 +70,13 @@ const FieldMappingComponent: React.FC = ({ const onChangeThirdParty = useCallback( (caseField: CaseField, newThirdPartyField: ThirdPartyField) => { const myMapping = mapping ?? defaultMapping; - onChangeMapping( - myMapping.map(item => { - if (item.source !== caseField && item.target === newThirdPartyField) { - return { ...item, target: 'not_mapped' }; - } else if (item.source === caseField) { - return { ...item, target: newThirdPartyField }; - } - return item; - }) - ); + onChangeMapping(setThirdPartyToMapping(caseField, newThirdPartyField, myMapping)); }, [mapping] ); return ( <> - + {i18n.FIELD_MAPPING_FIRST_COL} @@ -100,7 +89,7 @@ const FieldMappingComponent: React.FC = ({ - + {(mapping ?? defaultMapping).map(item => ( > = [ + { + value: 'short_description', + inputDisplay: {'Short Description'}, + 'data-test-subj': 'third-party-short-desc', + }, + { + value: 'description', + inputDisplay: {'Description'}, + 'data-test-subj': 'third-party-desc', + }, +]; + +describe('FieldMappingRow', () => { + let wrapper: ReactWrapper; + const onChangeActionType = jest.fn(); + const onChangeThirdParty = jest.fn(); + + const props: RowProps = { + disabled: false, + siemField: 'title', + thirdPartyOptions, + onChangeActionType, + onChangeThirdParty, + selectedActionType: 'nothing', + selectedThirdParty: 'short_description', + }; + + beforeAll(() => { + wrapper = mount(, { wrappingComponent: TestProviders }); + }); + + test('it renders', () => { + expect( + wrapper + .find('[data-test-subj="case-configure-third-party-select"]') + .first() + .exists() + ).toBe(true); + + expect( + wrapper + .find('[data-test-subj="case-configure-action-type-select"]') + .first() + .exists() + ).toBe(true); + }); + + test('it passes thirdPartyOptions correctly', () => { + const selectProps = wrapper + .find(EuiSuperSelect) + .first() + .props(); + + expect(selectProps.options).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + value: 'short_description', + 'data-test-subj': 'third-party-short-desc', + }), + expect.objectContaining({ + value: 'description', + 'data-test-subj': 'third-party-desc', + }), + ]) + ); + }); + + test('it passes the correct actionTypeOptions', () => { + const selectProps = wrapper + .find(EuiSuperSelect) + .at(1) + .props(); + + expect(selectProps.options).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + value: 'nothing', + 'data-test-subj': 'edit-update-option-nothing', + }), + expect.objectContaining({ + value: 'overwrite', + 'data-test-subj': 'edit-update-option-overwrite', + }), + expect.objectContaining({ + value: 'append', + 'data-test-subj': 'edit-update-option-append', + }), + ]) + ); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/field_mapping_row.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/field_mapping_row.tsx index 62e43c86af8d9e..732a11a58d35a7 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/field_mapping_row.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/field_mapping_row.tsx @@ -21,7 +21,7 @@ import { ThirdPartyField, } from '../../../../containers/case/configure/types'; -interface RowProps { +export interface RowProps { disabled: boolean; siemField: CaseField; thirdPartyOptions: Array>; @@ -77,6 +77,7 @@ const FieldMappingRowComponent: React.FC = ({ options={thirdPartyOptions} valueOfSelected={selectedThirdParty} onChange={onChangeThirdParty.bind(null, siemField)} + data-test-subj={'case-configure-third-party-select'} /> @@ -85,6 +86,7 @@ const FieldMappingRowComponent: React.FC = ({ options={actionTypeOptions} valueOfSelected={selectedActionType} onChange={onChangeActionType.bind(null, siemField)} + data-test-subj={'case-configure-action-type-select'} />
diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/index.test.tsx new file mode 100644 index 00000000000000..5ea3f500c0349f --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/index.test.tsx @@ -0,0 +1,748 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useEffect } from 'react'; +import { ReactWrapper, mount } from 'enzyme'; + +import { useKibana } from '../../../../lib/kibana'; +import { useConnectors } from '../../../../containers/case/configure/use_connectors'; +import { useCaseConfigure } from '../../../../containers/case/configure/use_configure'; +import { useGetUrlSearch } from '../../../../components/navigation/use_get_url_search'; + +import { + connectors, + searchURL, + useCaseConfigureResponse, + useConnectorsResponse, + kibanaMockImplementationArgs, +} from './__mock__'; + +jest.mock('../../../../lib/kibana'); +jest.mock('../../../../containers/case/configure/use_connectors'); +jest.mock('../../../../containers/case/configure/use_configure'); +jest.mock('../../../../components/navigation/use_get_url_search'); + +const useKibanaMock = useKibana as jest.Mock; +const useConnectorsMock = useConnectors as jest.Mock; +const useCaseConfigureMock = useCaseConfigure as jest.Mock; +const useGetUrlSearchMock = useGetUrlSearch as jest.Mock; + +import { ConfigureCases } from './'; +import { TestProviders } from '../../../../mock'; +import { Connectors } from './connectors'; +import { ClosureOptions } from './closure_options'; +import { Mapping } from './mapping'; +import { + ActionsConnectorsContextProvider, + ConnectorAddFlyout, + ConnectorEditFlyout, +} from '../../../../../../../../plugins/triggers_actions_ui/public'; +import { EuiBottomBar } from '@elastic/eui'; + +describe('rendering', () => { + let wrapper: ReactWrapper; + beforeEach(() => { + jest.resetAllMocks(); + useCaseConfigureMock.mockImplementation(() => useCaseConfigureResponse); + useConnectorsMock.mockImplementation(() => ({ ...useConnectorsResponse, connectors: [] })); + useKibanaMock.mockImplementation(() => kibanaMockImplementationArgs); + useGetUrlSearchMock.mockImplementation(() => searchURL); + + wrapper = mount(, { wrappingComponent: TestProviders }); + }); + + test('it renders the Connectors', () => { + expect(wrapper.find('[data-test-subj="case-connectors-form-group"]').exists()).toBeTruthy(); + }); + + test('it renders the ClosureType', () => { + expect( + wrapper.find('[data-test-subj="case-closure-options-form-group"]').exists() + ).toBeTruthy(); + }); + + test('it renders the Mapping', () => { + expect(wrapper.find('[data-test-subj="case-mapping-form-group"]').exists()).toBeTruthy(); + }); + + test('it renders the ActionsConnectorsContextProvider', () => { + // Components from triggers_actions_ui do not have a data-test-subj + expect(wrapper.find(ActionsConnectorsContextProvider).exists()).toBeTruthy(); + }); + + test('it renders the ConnectorAddFlyout', () => { + // Components from triggers_actions_ui do not have a data-test-subj + expect(wrapper.find(ConnectorAddFlyout).exists()).toBeTruthy(); + }); + + test('it does NOT render the ConnectorEditFlyout', () => { + // Components from triggers_actions_ui do not have a data-test-subj + expect(wrapper.find(ConnectorEditFlyout).exists()).toBeFalsy(); + }); + + test('it does NOT render the EuiCallOut', () => { + expect(wrapper.find('[data-test-subj="configure-cases-warning-callout"]').exists()).toBeFalsy(); + }); + + test('it does NOT render the EuiBottomBar', () => { + expect( + wrapper.find('[data-test-subj="case-configure-action-bottom-bar"]').exists() + ).toBeFalsy(); + }); +}); + +describe('ConfigureCases - Unhappy path', () => { + beforeEach(() => { + jest.resetAllMocks(); + useCaseConfigureMock.mockImplementation(() => useCaseConfigureResponse); + useConnectorsMock.mockImplementation(() => ({ ...useConnectorsResponse, connectors: [] })); + useKibanaMock.mockImplementation(() => kibanaMockImplementationArgs); + useGetUrlSearchMock.mockImplementation(() => searchURL); + }); + + test('it shows the warning callout when configuration is invalid', () => { + useCaseConfigureMock.mockImplementation( + ({ setConnector, setClosureType, setCurrentConfiguration }) => { + useEffect(() => setConnector('not-id'), []); + return useCaseConfigureResponse; + } + ); + + const wrapper = mount(, { wrappingComponent: TestProviders }); + + expect( + wrapper.find('[data-test-subj="configure-cases-warning-callout"]').exists() + ).toBeTruthy(); + }); +}); + +describe('ConfigureCases - Happy path', () => { + let wrapper: ReactWrapper; + + beforeEach(() => { + jest.resetAllMocks(); + useCaseConfigureMock.mockImplementation( + ({ setConnector, setClosureType, setCurrentConfiguration }) => { + useEffect(() => setConnector('123'), []); + useEffect(() => setClosureType('close-by-user'), []); + useEffect( + () => + setCurrentConfiguration({ + connectorId: '123', + closureType: 'close-by-user', + }), + [] + ); + return useCaseConfigureResponse; + } + ); + useConnectorsMock.mockImplementation(() => useConnectorsResponse); + useKibanaMock.mockImplementation(() => kibanaMockImplementationArgs); + useGetUrlSearchMock.mockImplementation(() => searchURL); + + wrapper = mount(, { wrappingComponent: TestProviders }); + }); + + test('it renders the ConnectorEditFlyout', () => { + expect(wrapper.find(ConnectorEditFlyout).exists()).toBeTruthy(); + }); + + test('it renders with correct props', () => { + // Connector + expect(wrapper.find(Connectors).prop('connectors')).toEqual(connectors); + expect(wrapper.find(Connectors).prop('disabled')).toBe(false); + expect(wrapper.find(Connectors).prop('isLoading')).toBe(false); + expect(wrapper.find(Connectors).prop('selectedConnector')).toBe('123'); + + // ClosureOptions + expect(wrapper.find(ClosureOptions).prop('disabled')).toBe(false); + expect(wrapper.find(ClosureOptions).prop('closureTypeSelected')).toBe('close-by-user'); + + // Mapping + expect(wrapper.find(Mapping).prop('disabled')).toBe(true); + expect(wrapper.find(Mapping).prop('updateConnectorDisabled')).toBe(false); + expect(wrapper.find(Mapping).prop('mapping')).toEqual( + connectors[0].config.casesConfiguration.mapping + ); + + // Flyouts + expect(wrapper.find(ConnectorAddFlyout).prop('addFlyoutVisible')).toBe(false); + expect(wrapper.find(ConnectorAddFlyout).prop('actionTypes')).toEqual([ + { + id: '.servicenow', + name: 'ServiceNow', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + minimumLicenseRequired: 'platinum', + }, + ]); + + expect(wrapper.find(ConnectorEditFlyout).prop('editFlyoutVisible')).toBe(false); + expect(wrapper.find(ConnectorEditFlyout).prop('initialConnector')).toEqual(connectors[0]); + }); + + test('it disables correctly when the user cannot crud', () => { + const newWrapper = mount(, { + wrappingComponent: TestProviders, + }); + + expect(newWrapper.find(Connectors).prop('disabled')).toBe(true); + expect(newWrapper.find(ClosureOptions).prop('disabled')).toBe(true); + expect(newWrapper.find(Mapping).prop('disabled')).toBe(true); + expect(newWrapper.find(Mapping).prop('updateConnectorDisabled')).toBe(true); + }); + + test('it disables correctly Connector when loading connectors', () => { + useConnectorsMock.mockImplementation(() => ({ + ...useConnectorsResponse, + loading: true, + })); + + const newWrapper = mount(, { wrappingComponent: TestProviders }); + + expect(newWrapper.find(Connectors).prop('disabled')).toBe(true); + }); + + test('it disables correctly Connector when saving configuration', () => { + useCaseConfigureMock.mockImplementation(() => ({ + ...useCaseConfigureResponse, + persistLoading: true, + })); + + const newWrapper = mount(, { wrappingComponent: TestProviders }); + + expect(newWrapper.find(Connectors).prop('disabled')).toBe(true); + }); + + test('it pass the correct value to isLoading attribute on Connector', () => { + useConnectorsMock.mockImplementation(() => ({ + ...useConnectorsResponse, + loading: true, + })); + + const newWrapper = mount(, { wrappingComponent: TestProviders }); + + expect(newWrapper.find(Connectors).prop('isLoading')).toBe(true); + }); + + test('it set correctly the selected connector', () => { + useCaseConfigureMock.mockImplementation( + ({ setConnector, setClosureType, setCurrentConfiguration }) => { + useEffect(() => setConnector('456'), []); + return useCaseConfigureResponse; + } + ); + + const newWrapper = mount(, { wrappingComponent: TestProviders }); + + expect(newWrapper.find(Connectors).prop('selectedConnector')).toBe('456'); + }); + + test('it show the add flyout when pressing the add connector button', () => { + wrapper.find('button[data-test-subj="case-configure-add-connector-button"]').simulate('click'); + wrapper.update(); + + expect(wrapper.find(ConnectorAddFlyout).prop('addFlyoutVisible')).toBe(true); + expect(wrapper.find(EuiBottomBar).exists()).toBeFalsy(); + }); + + test('it disables correctly ClosureOptions when loading connectors', () => { + useConnectorsMock.mockImplementation(() => ({ + ...useConnectorsResponse, + loading: true, + })); + + const newWrapper = mount(, { wrappingComponent: TestProviders }); + + expect(newWrapper.find(ClosureOptions).prop('disabled')).toBe(true); + }); + + test('it disables correctly ClosureOptions when saving configuration', () => { + useCaseConfigureMock.mockImplementation(() => ({ + ...useCaseConfigureResponse, + persistLoading: true, + })); + + const newWrapper = mount(, { wrappingComponent: TestProviders }); + + expect(newWrapper.find(ClosureOptions).prop('disabled')).toBe(true); + }); + + test('it disables correctly ClosureOptions when the connector is set to none', () => { + useCaseConfigureMock.mockImplementation( + ({ setConnector, setClosureType, setCurrentConfiguration }) => { + useEffect(() => setConnector('none'), []); + return useCaseConfigureResponse; + } + ); + + const newWrapper = mount(, { wrappingComponent: TestProviders }); + + expect(newWrapper.find(ClosureOptions).prop('disabled')).toBe(true); + }); + + test('it disables the mapping permanently', () => { + expect(wrapper.find(Mapping).prop('disabled')).toBe(true); + }); + + test('it disables the update connector button when loading the connectors', () => { + useConnectorsMock.mockImplementation(() => ({ + ...useConnectorsResponse, + loading: true, + })); + + expect(wrapper.find(Mapping).prop('disabled')).toBe(true); + }); + + test('it disables the update connector button when loading the configuration', () => { + useCaseConfigureMock.mockImplementation(() => ({ + ...useCaseConfigureResponse, + loading: true, + })); + + const newWrapper = mount(, { wrappingComponent: TestProviders }); + + expect(newWrapper.find(Mapping).prop('disabled')).toBe(true); + }); + + test('it disables the update connector button when saving the configuration', () => { + useCaseConfigureMock.mockImplementation(() => ({ + ...useCaseConfigureResponse, + persistLoading: true, + })); + + const newWrapper = mount(, { wrappingComponent: TestProviders }); + + expect(newWrapper.find(Mapping).prop('disabled')).toBe(true); + }); + + test('it disables the update connector button when the connectorId is invalid', () => { + useCaseConfigureMock.mockImplementation( + ({ setConnector, setClosureType, setCurrentConfiguration }) => { + useEffect(() => setConnector('not-id'), []); + return useCaseConfigureResponse; + } + ); + + const newWrapper = mount(, { wrappingComponent: TestProviders }); + + expect(newWrapper.find(Mapping).prop('disabled')).toBe(true); + }); + + test('it disables the update connector button when the connectorId is set to none', () => { + useCaseConfigureMock.mockImplementation( + ({ setConnector, setClosureType, setCurrentConfiguration }) => { + useEffect(() => setConnector('none'), []); + return useCaseConfigureResponse; + } + ); + + const newWrapper = mount(, { wrappingComponent: TestProviders }); + + expect(newWrapper.find(Mapping).prop('disabled')).toBe(true); + }); + + test('it show the edit flyout when pressing the update connector button', () => { + wrapper.find('button[data-test-subj="case-mapping-update-connector-button"]').simulate('click'); + wrapper.update(); + + expect(wrapper.find(ConnectorEditFlyout).prop('editFlyoutVisible')).toBe(true); + expect(wrapper.find(EuiBottomBar).exists()).toBeFalsy(); + }); + + test('it sets the mapping of a connector correctly', () => { + expect(wrapper.find(Mapping).prop('mapping')).toEqual( + connectors[0].config.casesConfiguration.mapping + ); + }); + + // TODO: When mapping is enabled the test.todo should be implemented. + test.todo('the mapping is changed successfully when changing the third party'); + test.todo('the mapping is changed successfully when changing the action type'); + + test('it does not shows the action bar when there is no change', () => { + expect( + wrapper.find('[data-test-subj="case-configure-action-bottom-bar"]').exists() + ).toBeFalsy(); + }); + + test('it shows the action bar when the connector is changed', () => { + wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); + wrapper.update(); + wrapper.find('button[data-test-subj="dropdown-connector-456"]').simulate('click'); + wrapper.update(); + + expect( + wrapper.find('[data-test-subj="case-configure-action-bottom-bar"]').exists() + ).toBeTruthy(); + expect( + wrapper + .find('[data-test-subj="case-configure-action-bottom-bar-total-changes"]') + .first() + .text() + ).toBe('1 unsaved changes'); + }); + + test('it shows the action bar when the closure type is changed', () => { + wrapper.find('input[id="close-by-pushing"]').simulate('change'); + wrapper.update(); + + expect( + wrapper.find('[data-test-subj="case-configure-action-bottom-bar"]').exists() + ).toBeTruthy(); + expect( + wrapper + .find('[data-test-subj="case-configure-action-bottom-bar-total-changes"]') + .first() + .text() + ).toBe('1 unsaved changes'); + }); + + test('it tracks the changes successfully', () => { + wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); + wrapper.update(); + wrapper.find('button[data-test-subj="dropdown-connector-456"]').simulate('click'); + wrapper.update(); + wrapper.find('input[id="close-by-pushing"]').simulate('change'); + wrapper.update(); + + expect( + wrapper.find('[data-test-subj="case-configure-action-bottom-bar"]').exists() + ).toBeTruthy(); + expect( + wrapper + .find('[data-test-subj="case-configure-action-bottom-bar-total-changes"]') + .first() + .text() + ).toBe('2 unsaved changes'); + }); + + test('it tracks and reverts the changes successfully ', () => { + // change settings + wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); + wrapper.update(); + wrapper.find('button[data-test-subj="dropdown-connector-456"]').simulate('click'); + wrapper.update(); + wrapper.find('input[id="close-by-pushing"]').simulate('change'); + wrapper.update(); + + // revert back to initial settings + wrapper.find('button[data-test-subj="dropdown-connectors"]').simulate('click'); + wrapper.update(); + wrapper.find('button[data-test-subj="dropdown-connector-123"]').simulate('click'); + wrapper.update(); + wrapper.find('input[id="close-by-user"]').simulate('change'); + wrapper.update(); + + expect( + wrapper.find('[data-test-subj="case-configure-action-bottom-bar"]').exists() + ).toBeFalsy(); + }); + + test('it close and restores the action bar when the add connector button is pressed', () => { + // Change closure type + wrapper.find('input[id="close-by-pushing"]').simulate('change'); + wrapper.update(); + + // Press add connector button + wrapper.find('button[data-test-subj="case-configure-add-connector-button"]').simulate('click'); + wrapper.update(); + + expect( + wrapper.find('[data-test-subj="case-configure-action-bottom-bar"]').exists() + ).toBeFalsy(); + + expect(wrapper.find(ConnectorAddFlyout).prop('addFlyoutVisible')).toBe(true); + + // Close the add flyout + wrapper.find('button[data-test-subj="euiFlyoutCloseButton"]').simulate('click'); + wrapper.update(); + + expect( + wrapper.find('[data-test-subj="case-configure-action-bottom-bar"]').exists() + ).toBeTruthy(); + + expect(wrapper.find(ConnectorAddFlyout).prop('addFlyoutVisible')).toBe(false); + + expect( + wrapper + .find('[data-test-subj="case-configure-action-bottom-bar-total-changes"]') + .first() + .text() + ).toBe('1 unsaved changes'); + }); + + test('it close and restores the action bar when the update connector button is pressed', () => { + // Change closure type + wrapper.find('input[id="close-by-pushing"]').simulate('change'); + wrapper.update(); + + // Press update connector button + wrapper.find('button[data-test-subj="case-mapping-update-connector-button"]').simulate('click'); + wrapper.update(); + + expect( + wrapper.find('[data-test-subj="case-configure-action-bottom-bar"]').exists() + ).toBeFalsy(); + + expect(wrapper.find(ConnectorEditFlyout).prop('editFlyoutVisible')).toBe(true); + + // Close the edit flyout + wrapper.find('button[data-test-subj="euiFlyoutCloseButton"]').simulate('click'); + wrapper.update(); + + expect( + wrapper.find('[data-test-subj="case-configure-action-bottom-bar"]').exists() + ).toBeTruthy(); + + expect(wrapper.find(ConnectorEditFlyout).prop('editFlyoutVisible')).toBe(false); + + expect( + wrapper + .find('[data-test-subj="case-configure-action-bottom-bar-total-changes"]') + .first() + .text() + ).toBe('1 unsaved changes'); + }); + + test('it disables the buttons of action bar when loading connectors', () => { + useCaseConfigureMock.mockImplementation( + ({ setConnector, setClosureType, setCurrentConfiguration }) => { + useEffect(() => setConnector('456'), []); + useEffect(() => setClosureType('close-by-user'), []); + useEffect( + () => + setCurrentConfiguration({ + connectorId: '123', + closureType: 'close-by-user', + }), + [] + ); + return useCaseConfigureResponse; + } + ); + + useConnectorsMock.mockImplementation(() => ({ + ...useConnectorsResponse, + loading: true, + })); + + const newWrapper = mount(, { wrappingComponent: TestProviders }); + + expect( + newWrapper + .find('[data-test-subj="case-configure-action-bottom-bar-cancel-button"]') + .first() + .prop('isDisabled') + ).toBe(true); + + expect( + newWrapper + .find('[data-test-subj="case-configure-action-bottom-bar-save-button"]') + .first() + .prop('isDisabled') + ).toBe(true); + }); + + test('it disables the buttons of action bar when loading configuration', () => { + useCaseConfigureMock.mockImplementation( + ({ setConnector, setClosureType, setCurrentConfiguration }) => { + useEffect(() => setConnector('456'), []); + useEffect(() => setClosureType('close-by-user'), []); + useEffect( + () => + setCurrentConfiguration({ + connectorId: '123', + closureType: 'close-by-user', + }), + [] + ); + return { ...useCaseConfigureResponse, loading: true }; + } + ); + + const newWrapper = mount(, { wrappingComponent: TestProviders }); + + expect( + newWrapper + .find('[data-test-subj="case-configure-action-bottom-bar-cancel-button"]') + .first() + .prop('isDisabled') + ).toBe(true); + + expect( + newWrapper + .find('[data-test-subj="case-configure-action-bottom-bar-save-button"]') + .first() + .prop('isDisabled') + ).toBe(true); + }); + + test('it disables the buttons of action bar when saving configuration', () => { + useCaseConfigureMock.mockImplementation( + ({ setConnector, setClosureType, setCurrentConfiguration }) => { + useEffect(() => setConnector('456'), []); + useEffect(() => setClosureType('close-by-user'), []); + useEffect( + () => + setCurrentConfiguration({ + connectorId: '123', + closureType: 'close-by-user', + }), + [] + ); + return { ...useCaseConfigureResponse, persistLoading: true }; + } + ); + + const newWrapper = mount(, { wrappingComponent: TestProviders }); + + expect( + newWrapper + .find('[data-test-subj="case-configure-action-bottom-bar-cancel-button"]') + .first() + .prop('isDisabled') + ).toBe(true); + + expect( + newWrapper + .find('[data-test-subj="case-configure-action-bottom-bar-save-button"]') + .first() + .prop('isDisabled') + ).toBe(true); + }); + + test('it shows the loading spinner when saving configuration', () => { + useCaseConfigureMock.mockImplementation( + ({ setConnector, setClosureType, setCurrentConfiguration }) => { + useEffect(() => setConnector('456'), []); + useEffect(() => setClosureType('close-by-user'), []); + useEffect( + () => + setCurrentConfiguration({ + connectorId: '123', + closureType: 'close-by-user', + }), + [] + ); + return { ...useCaseConfigureResponse, persistLoading: true }; + } + ); + + const newWrapper = mount(, { wrappingComponent: TestProviders }); + + expect( + newWrapper + .find('[data-test-subj="case-configure-action-bottom-bar-cancel-button"]') + .first() + .prop('isLoading') + ).toBe(true); + + expect( + newWrapper + .find('[data-test-subj="case-configure-action-bottom-bar-save-button"]') + .first() + .prop('isLoading') + ).toBe(true); + }); + + test('it closes the action bar when pressing save', () => { + useCaseConfigureMock.mockImplementation( + ({ setConnector, setClosureType, setCurrentConfiguration }) => { + useEffect(() => setConnector('456'), []); + useEffect(() => setClosureType('close-by-user'), []); + useEffect( + () => + setCurrentConfiguration({ + connectorId: '123', + closureType: 'close-by-user', + }), + [] + ); + return useCaseConfigureResponse; + } + ); + + const newWrapper = mount(, { wrappingComponent: TestProviders }); + + newWrapper + .find('[data-test-subj="case-configure-action-bottom-bar-save-button"]') + .first() + .simulate('click'); + + newWrapper.update(); + + expect( + newWrapper.find('[data-test-subj="case-configure-action-bottom-bar"]').exists() + ).toBeFalsy(); + }); + + test('it submits the configuration correctly', () => { + const persistCaseConfigure = jest.fn(); + + useCaseConfigureMock.mockImplementation( + ({ setConnector, setClosureType, setCurrentConfiguration }) => { + useEffect(() => setConnector('456'), []); + useEffect(() => setClosureType('close-by-user'), []); + useEffect( + () => + setCurrentConfiguration({ + connectorId: '123', + closureType: 'close-by-pushing', + }), + [] + ); + return { ...useCaseConfigureResponse, persistCaseConfigure }; + } + ); + + const newWrapper = mount(, { wrappingComponent: TestProviders }); + + newWrapper + .find('[data-test-subj="case-configure-action-bottom-bar-save-button"]') + .first() + .simulate('click'); + + newWrapper.update(); + + expect(persistCaseConfigure).toHaveBeenCalled(); + expect(persistCaseConfigure).toHaveBeenCalledWith({ + connectorId: '456', + connectorName: 'My Connector 2', + closureType: 'close-by-user', + }); + }); + + test('it has the correct url on cancel button', () => { + const persistCaseConfigure = jest.fn(); + + useCaseConfigureMock.mockImplementation( + ({ setConnector, setClosureType, setCurrentConfiguration }) => { + useEffect(() => setConnector('456'), []); + useEffect(() => setClosureType('close-by-user'), []); + useEffect( + () => + setCurrentConfiguration({ + connectorId: '123', + closureType: 'close-by-user', + }), + [] + ); + return { ...useCaseConfigureResponse, persistCaseConfigure }; + } + ); + + const newWrapper = mount(, { wrappingComponent: TestProviders }); + + expect( + newWrapper + .find('[data-test-subj="case-configure-action-bottom-bar-cancel-button"]') + .first() + .prop('href') + ).toBe(`#/link-to/case${searchURL}`); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/index.tsx index b8cf5a38808011..241dcef14a145d 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/index.tsx @@ -140,6 +140,7 @@ const ConfigureCasesComponent: React.FC = ({ userC setClosureType, setCurrentConfiguration, }); + const { loading: isLoadingConnectors, connectors, refetchConnectors } = useConnectors(); // ActionsConnectorsContextProvider reloadConnectors prop expects a Promise. @@ -251,7 +252,12 @@ const ConfigureCasesComponent: React.FC = ({ userC {!connectorIsValid && ( - + {i18n.WARNING_NO_CONNECTOR_MESSAGE} @@ -283,11 +289,13 @@ const ConfigureCasesComponent: React.FC = ({ userC /> {actionBarVisible && ( - + - {i18n.UNSAVED_CHANGES(totalConfigurationChanges)} + + {i18n.UNSAVED_CHANGES(totalConfigurationChanges)} + @@ -300,6 +308,7 @@ const ConfigureCasesComponent: React.FC = ({ userC isLoading={persistLoading} aria-label={i18n.CANCEL} href={getCaseUrl(search)} + data-test-subj="case-configure-action-bottom-bar-cancel-button" > {i18n.CANCEL} @@ -313,6 +322,7 @@ const ConfigureCasesComponent: React.FC = ({ userC isDisabled={isLoadingAny} isLoading={persistLoading} onClick={handleSubmit} + data-test-subj="case-configure-action-bottom-bar-save-button" > {i18n.SAVE_CHANGES} diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/mapping.test.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/mapping.test.tsx new file mode 100644 index 00000000000000..fefcb2ca8cf6a3 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/mapping.test.tsx @@ -0,0 +1,65 @@ +/* + * 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, ReactWrapper } from 'enzyme'; + +import { TestProviders } from '../../../../mock'; +import { Mapping, MappingProps } from './mapping'; +import { mapping } from './__mock__'; + +describe('Mapping', () => { + let wrapper: ReactWrapper; + const onChangeMapping = jest.fn(); + const setEditFlyoutVisibility = jest.fn(); + const props: MappingProps = { + disabled: false, + mapping, + updateConnectorDisabled: false, + onChangeMapping, + setEditFlyoutVisibility, + }; + + beforeAll(() => { + wrapper = mount(, { wrappingComponent: TestProviders }); + }); + + test('it shows mapping form group', () => { + expect( + wrapper + .find('[data-test-subj="case-mapping-form-group"]') + .first() + .exists() + ).toBe(true); + }); + + test('it shows mapping form row', () => { + expect( + wrapper + .find('[data-test-subj="case-mapping-form-row"]') + .first() + .exists() + ).toBe(true); + }); + + test('it shows the update button', () => { + expect( + wrapper + .find('[data-test-subj="case-mapping-update-connector-button"]') + .first() + .exists() + ).toBe(true); + }); + + test('it shows the field mapping', () => { + expect( + wrapper + .find('[data-test-subj="case-mapping-field"]') + .first() + .exists() + ).toBe(true); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/mapping.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/mapping.tsx index 8cba73d1249df1..7340a49f6d0bbc 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/mapping.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/mapping.tsx @@ -20,7 +20,7 @@ import * as i18n from './translations'; import { FieldMapping } from './field_mapping'; import { CasesConfigurationMapping } from '../../../../containers/case/configure/types'; -interface MappingProps { +export interface MappingProps { disabled: boolean; updateConnectorDisabled: boolean; mapping: CasesConfigurationMapping[] | null; @@ -45,20 +45,27 @@ const MappingComponent: React.FC = ({ fullWidth title={

{i18n.FIELD_MAPPING_TITLE}

} description={i18n.FIELD_MAPPING_DESC} + data-test-subj="case-mapping-form-group" > - + {i18n.UPDATE_CONNECTOR} - + ); }; diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/reducer.test.ts b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/reducer.test.ts new file mode 100644 index 00000000000000..df958b75dc6b89 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/reducer.test.ts @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { configureCasesReducer, Action, State } from './reducer'; +import { initialState, mapping } from './__mock__'; + +describe('Reducer', () => { + let reducer: (state: State, action: Action) => State; + + beforeAll(() => { + reducer = configureCasesReducer(); + }); + + test('it should set the correct configuration', () => { + const action: Action = { + type: 'setCurrentConfiguration', + currentConfiguration: { connectorId: '123', closureType: 'close-by-user' }, + }; + const state = reducer(initialState, action); + + expect(state).toEqual({ + ...state, + currentConfiguration: action.currentConfiguration, + }); + }); + + test('it should set the correct connector id', () => { + const action: Action = { + type: 'setConnectorId', + connectorId: '456', + }; + const state = reducer(initialState, action); + + expect(state).toEqual({ + ...state, + connectorId: action.connectorId, + }); + }); + + test('it should set the closure type', () => { + const action: Action = { + type: 'setClosureType', + closureType: 'close-by-pushing', + }; + const state = reducer(initialState, action); + + expect(state).toEqual({ + ...state, + closureType: action.closureType, + }); + }); + + test('it should set the mapping', () => { + const action: Action = { + type: 'setMapping', + mapping, + }; + const state = reducer(initialState, action); + + expect(state).toEqual({ + ...state, + mapping: action.mapping, + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/utils.test.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/utils.test.tsx new file mode 100644 index 00000000000000..1c6fc9b2d405f9 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/utils.test.tsx @@ -0,0 +1,63 @@ +/* + * 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 { mapping } from './__mock__'; +import { setActionTypeToMapping, setThirdPartyToMapping } from './utils'; +import { CasesConfigurationMapping } from '../../../../containers/case/configure/types'; + +describe('FieldMappingRow', () => { + test('it should change the action type', () => { + const newMapping = setActionTypeToMapping('title', 'nothing', mapping); + expect(newMapping[0].actionType).toBe('nothing'); + }); + + test('it should not change other fields', () => { + const [newTitle, description, comments] = setActionTypeToMapping('title', 'nothing', mapping); + expect(newTitle).not.toEqual(mapping[0]); + expect(description).toEqual(mapping[1]); + expect(comments).toEqual(mapping[2]); + }); + + test('it should return a new array when changing action type', () => { + const newMapping = setActionTypeToMapping('title', 'nothing', mapping); + expect(newMapping).not.toBe(mapping); + }); + + test('it should change the third party', () => { + const newMapping = setThirdPartyToMapping('title', 'description', mapping); + expect(newMapping[0].target).toBe('description'); + }); + + test('it should not change other fields when there is not a conflict', () => { + const tempMapping: CasesConfigurationMapping[] = [ + { + source: 'title', + target: 'short_description', + actionType: 'overwrite', + }, + { + source: 'comments', + target: 'comments', + actionType: 'append', + }, + ]; + + const [newTitle, comments] = setThirdPartyToMapping('title', 'description', tempMapping); + + expect(newTitle).not.toEqual(mapping[0]); + expect(comments).toEqual(tempMapping[1]); + }); + + test('it should return a new array when changing third party', () => { + const newMapping = setThirdPartyToMapping('title', 'description', mapping); + expect(newMapping).not.toBe(mapping); + }); + + test('it should change the target of the conflicting third party field to not_mapped', () => { + const newMapping = setThirdPartyToMapping('title', 'description', mapping); + expect(newMapping[1].target).toBe('not_mapped'); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/utils.ts b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/utils.ts new file mode 100644 index 00000000000000..2ac6cc1a385872 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/configure_cases/utils.ts @@ -0,0 +1,44 @@ +/* + * 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 { + CaseField, + ActionType, + CasesConfigurationMapping, + ThirdPartyField, +} from '../../../../containers/case/configure/types'; + +export const setActionTypeToMapping = ( + caseField: CaseField, + newActionType: ActionType, + mapping: CasesConfigurationMapping[] +): CasesConfigurationMapping[] => { + const findItemIndex = mapping.findIndex(item => item.source === caseField); + + if (findItemIndex >= 0) { + return [ + ...mapping.slice(0, findItemIndex), + { ...mapping[findItemIndex], actionType: newActionType }, + ...mapping.slice(findItemIndex + 1), + ]; + } + + return [...mapping]; +}; + +export const setThirdPartyToMapping = ( + caseField: CaseField, + newThirdPartyField: ThirdPartyField, + mapping: CasesConfigurationMapping[] +): CasesConfigurationMapping[] => + mapping.map(item => { + if (item.source !== caseField && item.target === newThirdPartyField) { + return { ...item, target: 'not_mapped' }; + } else if (item.source === caseField) { + return { ...item, target: newThirdPartyField }; + } + return item; + }); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/batch_actions.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/batch_actions.tsx index 60ad68b8c91415..454ef18e0ae143 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/batch_actions.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/batch_actions.tsx @@ -14,13 +14,15 @@ import { enableRulesAction, exportRulesAction, } from './actions'; -import { ActionToaster } from '../../../../components/toasters'; +import { ActionToaster, displayWarningToast } from '../../../../components/toasters'; import { Rule } from '../../../../containers/detection_engine/rules'; +import * as detectionI18n from '../../translations'; interface GetBatchItems { closePopover: () => void; dispatch: Dispatch; dispatchToaster: Dispatch; + hasMlPermissions: boolean; loadingRuleIds: string[]; reFetchRules: (refreshPrePackagedRule?: boolean) => void; rules: Rule[]; @@ -31,6 +33,7 @@ export const getBatchItems = ({ closePopover, dispatch, dispatchToaster, + hasMlPermissions, loadingRuleIds, reFetchRules, rules, @@ -57,7 +60,22 @@ export const getBatchItems = ({ const deactivatedIds = selectedRuleIds.filter( id => !rules.find(r => r.id === id)?.enabled ?? false ); - await enableRulesAction(deactivatedIds, true, dispatch, dispatchToaster); + + const deactivatedIdsNoML = deactivatedIds.filter( + id => rules.find(r => r.id === id)?.type !== 'machine_learning' ?? false + ); + + const mlRuleCount = deactivatedIds.length - deactivatedIdsNoML.length; + if (!hasMlPermissions && mlRuleCount > 0) { + displayWarningToast(detectionI18n.ML_RULES_UNAVAILABLE(mlRuleCount), dispatchToaster); + } + + await enableRulesAction( + hasMlPermissions ? deactivatedIds : deactivatedIdsNoML, + true, + dispatch, + dispatchToaster + ); }} > {i18n.BATCH_ACTION_ACTIVATE_SELECTED} diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx index 9a84d33ab5fdf4..80e644f800334a 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx @@ -13,6 +13,7 @@ import { EuiTableActionsColumnType, EuiText, EuiHealth, + EuiToolTip, } from '@elastic/eui'; import { FormattedRelative } from '@kbn/i18n/react'; import * as H from 'history'; @@ -36,6 +37,8 @@ import { } from './actions'; import { Action } from './reducer'; import { LocalizedDateTooltip } from '../../../../components/localized_date_tooltip'; +import * as detectionI18n from '../../translations'; +import { isMlRule } from '../../../../../common/detection_engine/ml_helpers'; export const getActions = ( dispatch: React.Dispatch, @@ -88,6 +91,7 @@ interface GetColumns { dispatch: React.Dispatch; dispatchToaster: Dispatch; history: H.History; + hasMlPermissions: boolean; hasNoPermissions: boolean; loadingRuleIds: string[]; reFetchRules: (refreshPrePackagedRule?: boolean) => void; @@ -98,6 +102,7 @@ export const getColumns = ({ dispatch, dispatchToaster, history, + hasMlPermissions, hasNoPermissions, loadingRuleIds, reFetchRules, @@ -182,14 +187,25 @@ export const getColumns = ({ field: 'enabled', name: i18n.COLUMN_ACTIVATE, render: (value: Rule['enabled'], item: Rule) => ( - + + + ), sortable: true, width: '95px', diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx index ccdfd1ed1be38d..e96ed856208bde 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx @@ -40,6 +40,8 @@ import { getColumns, getMonitoringColumns } from './columns'; import { showRulesTable } from './helpers'; import { allRulesReducer, State } from './reducer'; import { RulesTableFilters } from './rules_table_filters/rules_table_filters'; +import { useMlCapabilities } from '../../../../components/ml_popover/hooks/use_ml_capabilities'; +import { hasMlAdminPermissions } from '../../../../components/ml/permissions/has_ml_admin_permissions'; const SORT_FIELD = 'enabled'; const initialState: State = { @@ -111,6 +113,11 @@ export const AllRules = React.memo( const { loading: isLoadingRulesStatuses, rulesStatuses } = useRulesStatuses(rules); const history = useHistory(); const [, dispatchToaster] = useStateToaster(); + const mlCapabilities = useMlCapabilities(); + + // TODO: Refactor license check + hasMlAdminPermissions to common check + const hasMlPermissions = + mlCapabilities.isPlatinumOrTrialLicense && hasMlAdminPermissions(mlCapabilities); const setRules = useCallback((newRules: Rule[], newPagination: Partial) => { dispatch({ @@ -145,6 +152,7 @@ export const AllRules = React.memo( closePopover, dispatch, dispatchToaster, + hasMlPermissions, loadingRuleIds, selectedRuleIds, reFetchRules: reFetchRulesData, @@ -152,7 +160,15 @@ export const AllRules = React.memo( })} /> ), - [dispatch, dispatchToaster, loadingRuleIds, reFetchRulesData, rules, selectedRuleIds] + [ + dispatch, + dispatchToaster, + hasMlPermissions, + loadingRuleIds, + reFetchRulesData, + rules, + selectedRuleIds, + ] ); const paginationMemo = useMemo( @@ -184,6 +200,7 @@ export const AllRules = React.memo( dispatch, dispatchToaster, history, + hasMlPermissions, hasNoPermissions, loadingRuleIds: loadingRulesAction != null && @@ -192,7 +209,15 @@ export const AllRules = React.memo( : [], reFetchRules: reFetchRulesData, }); - }, [dispatch, dispatchToaster, history, loadingRuleIds, loadingRulesAction, reFetchRulesData]); + }, [ + dispatch, + dispatchToaster, + hasMlPermissions, + history, + loadingRuleIds, + loadingRulesAction, + reFetchRulesData, + ]); const monitoringColumns = useMemo(() => getMonitoringColumns(), []); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/ml_job_description.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/ml_job_description.tsx index 8276aa3578563e..5a9593f1a6de29 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/ml_job_description.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/description_step/ml_job_description.tsx @@ -66,7 +66,9 @@ const Wrapper = styled.div` `; export const MlJobDescription: React.FC<{ job: SiemJob }> = ({ job }) => { - const jobUrl = useKibana().services.application.getUrlForApp('ml#/jobs'); + const jobUrl = useKibana().services.application.getUrlForApp( + `ml#/jobs?mlManagement=(jobId:${encodeURI(job.id)})` + ); return ( diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/ml_job_select/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/ml_job_select/index.tsx index 3d253b71b53d61..794edf0ab5de71 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/ml_job_select/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/ml_job_select/index.tsx @@ -4,37 +4,64 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useCallback } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiFlexGroup, EuiFlexItem, EuiFormRow, + EuiIcon, EuiLink, EuiSuperSelect, EuiText, } from '@elastic/eui'; +import styled from 'styled-components'; import { FieldHook, getFieldValidityAndErrorMessage } from '../../../../../shared_imports'; import { useSiemJobs } from '../../../../../components/ml_popover/hooks/use_siem_jobs'; import { useKibana } from '../../../../../lib/kibana'; -import { ML_JOB_SELECT_PLACEHOLDER_TEXT } from '../step_define_rule/translations'; +import { + ML_JOB_SELECT_PLACEHOLDER_TEXT, + ENABLE_ML_JOB_WARNING, +} from '../step_define_rule/translations'; +import { isJobStarted } from '../../../../../../common/detection_engine/ml_helpers'; + +const HelpTextWarningContainer = styled.div` + margin-top: 10px; +`; + +const MlJobSelectEuiFlexGroup = styled(EuiFlexGroup)` + margin-bottom: 5px; +`; -const HelpText: React.FC<{ href: string }> = ({ href }) => ( - - - - ), - }} - /> +const HelpText: React.FC<{ href: string; showEnableWarning: boolean }> = ({ + href, + showEnableWarning = false, +}) => ( + <> + + + + ), + }} + /> + {showEnableWarning && ( + + + + {ENABLE_ML_JOB_WARNING} + + + )} + ); const JobDisplay: React.FC<{ title: string; description: string }> = ({ title, description }) => ( @@ -77,26 +104,37 @@ export const MlJobSelect: React.FC = ({ describedByIds = [], f const options = [placeholderOption, ...jobOptions]; + const isJobRunning = useMemo(() => { + // If the selected job is not found in the list, it means the placeholder is selected + // and so we don't want to show the warning, thus isJobRunning will be true when 'job == null' + const job = siemJobs.find(j => j.id === jobId); + return job == null || isJobStarted(job.jobState, job.datafeedState); + }, [siemJobs, jobId]); + return ( - } - isInvalid={isInvalid} - error={errorMessage} - data-test-subj="mlJobSelect" - describedByIds={describedByIds} - > - - - - - - + + + } + isInvalid={isInvalid} + error={errorMessage} + data-test-subj="mlJobSelect" + describedByIds={describedByIds} + > + + + + + + + + ); }; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/translations.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/translations.tsx index 1d8821aceb2497..bbdb2130ce2983 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/translations.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/step_define_rule/translations.tsx @@ -62,3 +62,11 @@ export const ML_JOB_SELECT_PLACEHOLDER_TEXT = i18n.translate( defaultMessage: 'Select a job', } ); + +export const ENABLE_ML_JOB_WARNING = i18n.translate( + 'xpack.siem.detectionEngine.createRule.stepDefineRule.mlEnableJobWarningTitle', + { + defaultMessage: + 'This ML job is not currently running. Please set this job to run via "ML job settings" before activating this rule.', + } +); 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 efb601b6bd2072..8d793f39afa997 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 151e3a9bdf4d6d..7ad116c313361d 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/details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx index cb4d88a8bb539c..2b648a3b3f825f 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/details/index.tsx @@ -14,6 +14,7 @@ import { EuiSpacer, EuiTab, EuiTabs, + EuiToolTip, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { FC, memo, useCallback, useMemo, useState } from 'react'; @@ -66,6 +67,8 @@ import { RuleActionsOverflow } from '../components/rule_actions_overflow'; import { RuleStatusFailedCallOut } from './status_failed_callout'; import { FailureHistory } from './failure_history'; import { RuleStatus } from '../components/rule_status'; +import { useMlCapabilities } from '../../../../components/ml_popover/hooks/use_ml_capabilities'; +import { hasMlAdminPermissions } from '../../../../components/ml/permissions/has_ml_admin_permissions'; enum RuleDetailTabs { signals = 'signals', @@ -114,6 +117,11 @@ const RuleDetailsPageComponent: FC = ({ scheduleRuleData: null, }; const [lastSignals] = useSignalInfo({ ruleId }); + const mlCapabilities = useMlCapabilities(); + + // TODO: Refactor license check + hasMlAdminPermissions to common check + const hasMlPermissions = + mlCapabilities.isPlatinumOrTrialLicense && hasMlAdminPermissions(mlCapabilities); const title = isLoading === true || rule === null ? : rule.name; const subTitle = useMemo( @@ -259,13 +267,25 @@ const RuleDetailsPageComponent: FC = ({ > - + + + 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 db1f2298b5ea78..58a1b0fd3133e6 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/public/pages/detection_engine/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts index 39277b3d3c77ee..008d660be9d88c 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts @@ -86,3 +86,17 @@ export const USER_UNAUTHENTICATED_MSG_BODY = i18n.translate( 'You do not have the required permissions for viewing the detection engine. For more help, contact your administrator.', } ); + +export const ML_RULES_DISABLED_MESSAGE = i18n.translate( + 'xpack.siem.detectionEngine.mlRulesDisabledMessageTitle', + { + defaultMessage: 'ML rules require Platinum License and ML Admin Permissions', + } +); + +export const ML_RULES_UNAVAILABLE = (totalRules: number) => + i18n.translate('xpack.siem.detectionEngine.mlUnavailableTitle', { + values: { totalRules }, + defaultMessage: + '{totalRules} {totalRules, plural, =1 {rule requires} other {rules require}} Machine Learning to enable.', + }); 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 189c596a771256..c6923283bec16f 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 b973d4c5f4e98b..be0943c014225b 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/get_signals_count.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/get_signals_count.ts index 33cee6d074b706..7ff6a4e5164bd7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/get_signals_count.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/notifications/get_signals_count.ts @@ -4,63 +4,40 @@ * you may not use this file except in compliance with the Elastic License. */ -import moment from 'moment'; -import { getNotificationResultsLink } from './utils'; -import { NotificationExecutorOptions } from './types'; -import { parseScheduleDates } from '../signals/utils'; +import { AlertServices } from '../../../../../../../plugins/alerting/server'; import { buildSignalsSearchQuery } from './build_signals_query'; -interface SignalsCountResults { - signalsCount: string; - resultsLink: string; -} - interface GetSignalsCount { - from: Date | string; - to: Date | string; - ruleAlertId: string; + from?: string; + to?: string; ruleId: string; index: string; - kibanaSiemAppUrl: string | undefined; - callCluster: NotificationExecutorOptions['services']['callCluster']; + callCluster: AlertServices['callCluster']; +} + +interface CountResult { + count: number; } export const getSignalsCount = async ({ from, to, - ruleAlertId, ruleId, index, callCluster, - kibanaSiemAppUrl = '', -}: GetSignalsCount): Promise => { - const fromMoment = moment.isDate(from) ? moment(from) : parseScheduleDates(from); - const toMoment = moment.isDate(to) ? moment(to) : parseScheduleDates(to); - - if (!fromMoment || !toMoment) { - throw new Error(`There was an issue with parsing ${from} or ${to} into Moment object`); +}: GetSignalsCount): Promise => { + if (from == null || to == null) { + throw Error('"from" or "to" was not provided to signals count query'); } - const fromInMs = fromMoment.format('x'); - const toInMs = toMoment.format('x'); - const query = buildSignalsSearchQuery({ index, ruleId, - to: toInMs, - from: fromInMs, + to, + from, }); - const result = await callCluster('count', query); - const resultsLink = getNotificationResultsLink({ - kibanaSiemAppUrl: `${kibanaSiemAppUrl}`, - id: ruleAlertId, - from: fromInMs, - to: toInMs, - }); + const result: CountResult = await callCluster('count', query); - return { - signalsCount: result.count, - resultsLink, - }; + return result.count; }; 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 e74da583e91932..ced81098c9f8e5 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 @@ -13,6 +13,8 @@ import { getSignalsCount } from './get_signals_count'; import { RuleAlertAttributes } from '../signals/types'; import { siemRuleActionGroups } from '../signals/siem_rule_action_groups'; import { scheduleNotificationActions } from './schedule_notification_actions'; +import { getNotificationResultsLink } from './utils'; +import { parseScheduleDates } from '../signals/utils'; export const rulesNotificationAlertType = ({ logger, @@ -42,21 +44,31 @@ export const rulesNotificationAlertType = ({ const { params: ruleAlertParams, name: ruleName } = ruleAlertSavedObject.attributes; const ruleParams = { ...ruleAlertParams, name: ruleName, id: ruleAlertSavedObject.id }; - const { signalsCount, resultsLink } = await getSignalsCount({ - from: previousStartedAt ?? `now-${ruleParams.interval}`, - to: startedAt, + const fromInMs = parseScheduleDates( + previousStartedAt ? previousStartedAt.toISOString() : `now-${ruleParams.interval}` + )?.format('x'); + const toInMs = parseScheduleDates(startedAt.toISOString())?.format('x'); + + const signalsCount = await getSignalsCount({ + from: fromInMs, + to: toInMs, index: ruleParams.outputIndex, - ruleId: ruleParams.ruleId!, - kibanaSiemAppUrl: ruleAlertParams.meta?.kibanaSiemAppUrl as string, - ruleAlertId: ruleAlertSavedObject.id, + ruleId: ruleParams.ruleId, callCluster: services.callCluster, }); + const resultsLink = getNotificationResultsLink({ + from: fromInMs, + to: toInMs, + id: ruleAlertSavedObject.id, + 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 b858b25377ffe9..9f145af79ca90f 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,14 +8,14 @@ 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; }; interface ScheduleNotificationActions { alertInstance: AlertInstance; - signalsCount: string; + signalsCount: number; resultsLink: string; ruleParams: NotificationRuleTypeParams; } @@ -23,7 +23,7 @@ interface ScheduleNotificationActions { export const scheduleNotificationActions = ({ alertInstance, signalsCount, - resultsLink, + resultsLink = '', ruleParams, }: ScheduleNotificationActions): AlertInstance => alertInstance 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 b8a3c4199c4f0c..c91c4490e8eba0 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 @@ -5,14 +5,17 @@ */ export const getNotificationResultsLink = ({ - kibanaSiemAppUrl, + kibanaSiemAppUrl = '/app/siem', id, from, to, }: { - kibanaSiemAppUrl: string; + kibanaSiemAppUrl?: string; id: string; - from: string; - to: string; -}) => - `${kibanaSiemAppUrl}#/detections/rules/id/${id}?timerange=(global:(linkTo:!(timeline),timerange:(from:${from},kind:absolute,to:${to})),timeline:(linkTo:!(global),timerange:(from:${from},kind:absolute,to:${to})))`; + from?: string; + to?: string; +}) => { + if (from == null || to == null) return ''; + + return `${kibanaSiemAppUrl}#/detections/rules/id/${id}?timerange=(global:(linkTo:!(timeline),timerange:(from:${from},kind:absolute,to:${to})),timeline:(linkTo:!(global),timerange:(from:${from},kind:absolute,to:${to})))`; +}; 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 3b24b722f73561..e400360a5a5b25 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 63fe51f2c81cdc..c929b0718207d6 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/patch_rules_bulk_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts index 85255594ee4805..8c0fceb7a5f295 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 f553ccd2c6f81f..9c5000d70e5fe8 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/validate.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/rules/validate.test.ts index 77e05796fbcbe4..7537401e5a3667 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 b10627d151fa29..8c741c937bf159 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 08bd01ee9a1a00..e56e8e5fe34d31 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 c8e5bb981f921b..40f7b19ea12b3e 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 45b5028f392b9b..e01a8f40fcea4a 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 46cd1b653b5b44..d5ea950d163f57 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 538c8f754fd6ea..faae1dde835452 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 16e419f389f09e..c17ae8466a86c5 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 14df1c3d8cd55a..e8a9c7b0886a19 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 0e0944a11b4166..85a38e296494a1 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 6f6beea7fa5fbd..e8f9aad620ca01 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 8d7360bae8eb9c..e4015ad8bafa4d 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 23c99b36cb4a76..97cfc1d2d9ea79 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 @@ -14,7 +14,7 @@ interface CreateRuleActionsSavedObject { ruleAlertId: string; savedObjectsClient: AlertServices['savedObjectsClient']; actions: RuleAlertAction[] | undefined; - throttle: string | undefined; + throttle: string | null | undefined; } export const createRuleActionsSavedObject = async ({ @@ -22,7 +22,12 @@ export const createRuleActionsSavedObject = async ({ savedObjectsClient, actions = [], throttle, -}: CreateRuleActionsSavedObject) => { +}: CreateRuleActionsSavedObject): Promise<{ + id: string; + actions: RuleAlertAction[]; + alertThrottle: string | null; + ruleThrottle: string; +}> => { 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 4e8781dd456920..864281da5bafd2 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,7 +16,7 @@ interface DeleteRuleActionsSavedObject { export const deleteRuleActionsSavedObject = async ({ ruleAlertId, savedObjectsClient, -}: DeleteRuleActionsSavedObject) => { +}: DeleteRuleActionsSavedObject): Promise<{} | null> => { const ruleActions = await getRuleActionsSavedObject({ ruleAlertId, savedObjectsClient }); if (!ruleActions) 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 3ae9090526d693..61b544db5a18a4 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'; @@ -17,7 +18,12 @@ interface GetRuleActionsSavedObject { export const getRuleActionsSavedObject = async ({ ruleAlertId, savedObjectsClient, -}: GetRuleActionsSavedObject) => { +}: GetRuleActionsSavedObject): Promise<{ + id: string; + actions: RuleAlertAction[]; + alertThrottle: string | null; + ruleThrottle: string; +} | null> => { const { saved_objects } = await savedObjectsClient.find< IRuleActionsAttributesSavedObjectAttributes >({ 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 3856f75255262a..adc87150f89a75 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 ({ 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 56bce3c8b67a36..a15005110c56b1 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 @@ -15,7 +15,7 @@ interface DeleteRuleActionsSavedObject { ruleAlertId: string; savedObjectsClient: AlertServices['savedObjectsClient']; actions: RuleAlertAction[] | undefined; - throttle: string | undefined; + throttle: string | null | undefined; } export const updateRuleActionsSavedObject = async ({ @@ -23,7 +23,12 @@ export const updateRuleActionsSavedObject = async ({ savedObjectsClient, actions, throttle, -}: DeleteRuleActionsSavedObject) => { +}: DeleteRuleActionsSavedObject): Promise<{ + ruleThrottle: string; + alertThrottle: string | null; + actions: RuleAlertAction[]; + id: string; +} | null> => { const ruleActions = await getRuleActionsSavedObject({ ruleAlertId, savedObjectsClient }); if (!ruleActions) return null; 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 3c297ed8485552..554c2b4a3dd905 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/get_export_all.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/get_export_all.test.ts index ca6fb15e1fad90..dd004e3685b1d7 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 175c906f7996cf..715cb23e8444a9 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/prepackaged_rules/403_response_to_a_post.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/403_response_to_a_post.json index 3b043439759c10..d4118d0686b111 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/403_response_to_a_post.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/403_response_to_a_post.json @@ -20,5 +20,5 @@ "Elastic" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/405_response_method_not_allowed.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/405_response_method_not_allowed.json index 12c6a5feabebbf..da27f0a71d281b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/405_response_method_not_allowed.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/405_response_method_not_allowed.json @@ -20,5 +20,5 @@ "Elastic" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_adversary_behavior_detected.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_adversary_behavior_detected.json index a3302896b7e988..cfc322788d4be9 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_adversary_behavior_detected.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_adversary_behavior_detected.json @@ -7,7 +7,7 @@ "interval": "10m", "language": "kuery", "name": "Adversary Behavior - Detected - Elastic Endpoint", - "query": "event.kind:alert and event.module:endgame and event.action:rules_engine_event", + "query": "event.kind:alert and event.module:endgame and (event.action:rules_engine_event or endgame.event_subtype_full:rules_engine_event)", "risk_score": 47, "rule_id": "77a3c3df-8ec4-4da4-b758-878f551dee69", "severity": "medium", @@ -16,5 +16,5 @@ "Endpoint" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_detected.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_detected.json index 8c2c5f32feab70..0647fe9c9ce109 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_detected.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_detected.json @@ -7,7 +7,7 @@ "interval": "10m", "language": "kuery", "name": "Credential Dumping - Detected - Elastic Endpoint", - "query": "event.kind:alert and event.module:endgame and event.action:cred_theft_event and endgame.metadata.type:detection", + "query": "event.kind:alert and event.module:endgame and endgame.metadata.type:detection and (event.action:cred_theft_event or endgame.event_subtype_full:cred_theft_event)", "risk_score": 73, "rule_id": "571afc56-5ed9-465d-a2a9-045f099f6e7e", "severity": "high", @@ -16,5 +16,5 @@ "Endpoint" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_prevented.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_prevented.json index 6a96da3218bf25..036c88688d9bdd 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_prevented.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_dumping_prevented.json @@ -7,7 +7,7 @@ "interval": "10m", "language": "kuery", "name": "Credential Dumping - Prevented - Elastic Endpoint", - "query": "event.kind:alert and event.module:endgame and event.action:cred_theft_event and endgame.metadata.type:prevention", + "query": "event.kind:alert and event.module:endgame and endgame.metadata.type:prevention and (event.action:cred_theft_event or endgame.event_subtype_full:cred_theft_event)", "risk_score": 47, "rule_id": "db8c33a8-03cd-4988-9e2c-d0a4863adb13", "severity": "medium", @@ -16,5 +16,5 @@ "Endpoint" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_detected.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_detected.json index 954e35ccd644ae..0fe610d5511527 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_detected.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_detected.json @@ -7,7 +7,7 @@ "interval": "10m", "language": "kuery", "name": "Credential Manipulation - Detected - Elastic Endpoint", - "query": "event.kind:alert and event.module:endgame and event.action:token_manipulation_event and endgame.metadata.type:detection", + "query": "event.kind:alert and event.module:endgame and endgame.metadata.type:detection and (event.action:token_manipulation_event or endgame.event_subtype_full:token_manipulation_event)", "risk_score": 73, "rule_id": "c0be5f31-e180-48ed-aa08-96b36899d48f", "severity": "high", @@ -16,5 +16,5 @@ "Endpoint" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_prevented.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_prevented.json index 0de35891a3e818..a317c77bcd90aa 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_prevented.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_cred_manipulation_prevented.json @@ -7,7 +7,7 @@ "interval": "10m", "language": "kuery", "name": "Credential Manipulation - Prevented - Elastic Endpoint", - "query": "event.kind:alert and event.module:endgame and event.action:token_manipulation_event and endgame.metadata.type:prevention", + "query": "event.kind:alert and event.module:endgame and endgame.metadata.type:prevention and (event.action:token_manipulation_event or endgame.event_subtype_full:token_manipulation_event)", "risk_score": 47, "rule_id": "c9e38e64-3f4c-4bf3-ad48-0e61a60ea1fa", "severity": "medium", @@ -16,5 +16,5 @@ "Endpoint" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_detected.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_detected.json index 3652b7068ecd23..97640c0cea9b22 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_detected.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_detected.json @@ -7,7 +7,7 @@ "interval": "10m", "language": "kuery", "name": "Exploit - Detected - Elastic Endpoint", - "query": "event.kind:alert and event.module:endgame and event.action:exploit_event and endgame.metadata.type:detection", + "query": "event.kind:alert and event.module:endgame and endgame.metadata.type:detection and (event.action:exploit_event or endgame.event_subtype_full:exploit_event)", "risk_score": 73, "rule_id": "2003cdc8-8d83-4aa5-b132-1f9a8eb48514", "severity": "high", @@ -16,5 +16,5 @@ "Endpoint" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_prevented.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_prevented.json index dbc910c3002a73..069687a5af00fd 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_prevented.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_exploit_prevented.json @@ -7,7 +7,7 @@ "interval": "10m", "language": "kuery", "name": "Exploit - Prevented - Elastic Endpoint", - "query": "event.kind:alert and event.module:endgame and event.action:exploit_event and endgame.metadata.type:prevention", + "query": "event.kind:alert and event.module:endgame and endgame.metadata.type:prevention and (event.action:exploit_event or endgame.event_subtype_full:exploit_event)", "risk_score": 47, "rule_id": "2863ffeb-bf77-44dd-b7a5-93ef94b72036", "severity": "medium", @@ -16,5 +16,5 @@ "Endpoint" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_detected.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_detected.json index efe2806532be08..a7d3371190cedd 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_detected.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_detected.json @@ -7,7 +7,7 @@ "interval": "10m", "language": "kuery", "name": "Malware - Detected - Elastic Endpoint", - "query": "event.kind:alert and event.module:endgame and event.action:file_classification_event and endgame.metadata.type:detection", + "query": "event.kind:alert and event.module:endgame and endgame.metadata.type:detection and (event.action:file_classification_event or endgame.event_subtype_full:file_classification_event)", "risk_score": 99, "rule_id": "0a97b20f-4144-49ea-be32-b540ecc445de", "severity": "critical", @@ -16,5 +16,5 @@ "Endpoint" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_prevented.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_prevented.json index 51028b9dbeeb34..dd7bf72c34f900 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_prevented.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_malware_prevented.json @@ -7,7 +7,7 @@ "interval": "10m", "language": "kuery", "name": "Malware - Prevented - Elastic Endpoint", - "query": "event.kind:alert and event.module:endgame and event.action:file_classification_event and endgame.metadata.type:prevention", + "query": "event.kind:alert and event.module:endgame and endgame.metadata.type:prevention and (event.action:file_classification_event or endgame.event_subtype_full:file_classification_event)", "risk_score": 73, "rule_id": "3b382770-efbb-44f4-beed-f5e0a051b895", "severity": "high", @@ -16,5 +16,5 @@ "Endpoint" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_detected.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_detected.json index c30ca0632f410f..a8e102cc4619d1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_detected.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_detected.json @@ -7,7 +7,7 @@ "interval": "10m", "language": "kuery", "name": "Permission Theft - Detected - Elastic Endpoint", - "query": "event.kind:alert and event.module:endgame and event.action:token_protection_event and endgame.metadata.type:detection", + "query": "event.kind:alert and event.module:endgame and endgame.metadata.type:detection and (event.action:token_protection_event or endgame.event_subtype_full:token_protection_event)", "risk_score": 73, "rule_id": "c3167e1b-f73c-41be-b60b-87f4df707fe3", "severity": "high", @@ -16,5 +16,5 @@ "Endpoint" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_prevented.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_prevented.json index ed0c714254743e..c97330f2349eb6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_prevented.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_permission_theft_prevented.json @@ -7,7 +7,7 @@ "interval": "10m", "language": "kuery", "name": "Permission Theft - Prevented - Elastic Endpoint", - "query": "event.kind:alert and event.module:endgame and event.action:token_protection_event and endgame.metadata.type:prevention", + "query": "event.kind:alert and event.module:endgame and endgame.metadata.type:prevention and (event.action:token_protection_event or endgame.event_subtype_full:token_protection_event)", "risk_score": 47, "rule_id": "453f659e-0429-40b1-bfdb-b6957286e04b", "severity": "medium", @@ -16,5 +16,5 @@ "Endpoint" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_detected.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_detected.json index 63b008849487ab..e644c0e8d66eb4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_detected.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_detected.json @@ -7,7 +7,7 @@ "interval": "10m", "language": "kuery", "name": "Process Injection - Detected - Elastic Endpoint", - "query": "event.kind:alert and event.module:endgame and event.action:kernel_shellcode_event and endgame.metadata.type:detection", + "query": "event.kind:alert and event.module:endgame and endgame.metadata.type:detection and (event.action:kernel_shellcode_event or endgame.event_subtype_full:kernel_shellcode_event)", "risk_score": 73, "rule_id": "80c52164-c82a-402c-9964-852533d58be1", "severity": "high", @@ -16,5 +16,5 @@ "Endpoint" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_prevented.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_prevented.json index 135b4a95e8005e..61cbe267f9a465 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_prevented.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_process_injection_prevented.json @@ -7,7 +7,7 @@ "interval": "10m", "language": "kuery", "name": "Process Injection - Prevented - Elastic Endpoint", - "query": "event.kind:alert and event.module:endgame and event.action:kernel_shellcode_event and endgame.metadata.type:prevention", + "query": "event.kind:alert and event.module:endgame and endgame.metadata.type:prevention and (event.action:kernel_shellcode_event or endgame.event_subtype_full:kernel_shellcode_event)", "risk_score": 47, "rule_id": "990838aa-a953-4f3e-b3cb-6ddf7584de9e", "severity": "medium", @@ -16,5 +16,5 @@ "Endpoint" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_detected.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_detected.json index d4042a5e6b9e18..0e88b26cb2c758 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_detected.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_detected.json @@ -7,7 +7,7 @@ "interval": "10m", "language": "kuery", "name": "Ransomware - Detected - Elastic Endpoint", - "query": "event.kind:alert and event.module:endgame and event.action:ransomware_event and endgame.metadata.type:detection", + "query": "event.kind:alert and event.module:endgame and endgame.metadata.type:detection and (event.action:ransomware_event or endgame.event_subtype_full:ransomware_event)", "risk_score": 99, "rule_id": "8cb4f625-7743-4dfb-ae1b-ad92be9df7bd", "severity": "critical", @@ -16,5 +16,5 @@ "Endpoint" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_prevented.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_prevented.json index befdf611da223e..ba341f059f26d5 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_prevented.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/elastic_endpoint_security_ransomware_prevented.json @@ -7,7 +7,7 @@ "interval": "10m", "language": "kuery", "name": "Ransomware - Prevented - Elastic Endpoint", - "query": "event.kind:alert and event.module:endgame and event.action:ransomware_event and endgame.metadata.type:prevention", + "query": "event.kind:alert and event.module:endgame and endgame.metadata.type:prevention and (event.action:ransomware_event or endgame.event_subtype_full:ransomware_event)", "risk_score": 73, "rule_id": "e3c5d5cb-41d5-4206-805c-f30561eae3ac", "severity": "high", @@ -16,5 +16,5 @@ "Endpoint" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_adding_the_hidden_file_attribute_with_via_attribexe.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_adding_the_hidden_file_attribute_with_via_attribexe.json index 6c9b54b8ddb02b..25d2232d3f6dc5 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_adding_the_hidden_file_attribute_with_via_attribexe.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_adding_the_hidden_file_attribute_with_via_attribexe.json @@ -46,5 +46,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_clearing_windows_event_logs.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_clearing_windows_event_logs.json index 244d329cc4bb74..1c73d6c276ce6c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_clearing_windows_event_logs.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_clearing_windows_event_logs.json @@ -31,5 +31,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_delete_volume_usn_journal_with_fsutil.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_delete_volume_usn_journal_with_fsutil.json index 40875428165889..0bfa18398eada2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_delete_volume_usn_journal_with_fsutil.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_delete_volume_usn_journal_with_fsutil.json @@ -31,5 +31,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_deleting_backup_catalogs_with_wbadmin.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_deleting_backup_catalogs_with_wbadmin.json index eca06723e68b8d..e7293eda6390fd 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_deleting_backup_catalogs_with_wbadmin.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_deleting_backup_catalogs_with_wbadmin.json @@ -31,5 +31,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_direct_outbound_smb_connection.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_direct_outbound_smb_connection.json index e37c877c62889b..2896d27e19112f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_direct_outbound_smb_connection.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_direct_outbound_smb_connection.json @@ -31,5 +31,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_disable_windows_firewall_rules_with_netsh.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_disable_windows_firewall_rules_with_netsh.json index f6b4bc67ed9b13..42fe51f4e03737 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_disable_windows_firewall_rules_with_netsh.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_disable_windows_firewall_rules_with_netsh.json @@ -31,5 +31,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_encoding_or_decoding_files_via_certutil.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_encoding_or_decoding_files_via_certutil.json index 38162889737ff7..eef112503da5bc 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_encoding_or_decoding_files_via_certutil.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_encoding_or_decoding_files_via_certutil.json @@ -31,5 +31,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_scheduled_task_commands.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_scheduled_task_commands.json index 42007f153bd55d..dbacb2537e60f3 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_scheduled_task_commands.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_scheduled_task_commands.json @@ -34,5 +34,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_service_commands.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_service_commands.json index 9559baabe0e40c..648e83b4a5267f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_service_commands.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_local_service_commands.json @@ -31,5 +31,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_msbuild_making_network_connections.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_msbuild_making_network_connections.json index 3e34aacf605c7d..5e8b260d44b55c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_msbuild_making_network_connections.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_msbuild_making_network_connections.json @@ -31,5 +31,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_mshta_making_network_connections.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_mshta_making_network_connections.json index 769614e8faf53a..88bd248e258d88 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_mshta_making_network_connections.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_mshta_making_network_connections.json @@ -34,5 +34,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_psexec_lateral_movement_command.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_psexec_lateral_movement_command.json index ac170665042f66..f763d2aa033630 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_psexec_lateral_movement_command.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_psexec_lateral_movement_command.json @@ -49,5 +49,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_system_shells_via_services.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_system_shells_via_services.json index 1c001caa1539c3..f1b1879fc2652c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_system_shells_via_services.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_system_shells_via_services.json @@ -31,5 +31,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_network_connection_via_rundll32.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_network_connection_via_rundll32.json index 0165f4d7512e43..2a7960c939d01b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_network_connection_via_rundll32.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_network_connection_via_rundll32.json @@ -31,5 +31,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_parentchild_relationship.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_parentchild_relationship.json index 0b4bf9ff32945e..9a28c87c77089c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_parentchild_relationship.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_parentchild_relationship.json @@ -31,5 +31,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_process_network_connection.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_process_network_connection.json index 2c88a2061844c1..43a3d6f6af0b25 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_process_network_connection.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_unusual_process_network_connection.json @@ -31,5 +31,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_user_account_creation.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_user_account_creation.json index 240df34419132e..7054e7f67c3582 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_user_account_creation.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_user_account_creation.json @@ -31,5 +31,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_vssadmin.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_vssadmin.json index e12c2e70138c92..24f1cb72504f38 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_vssadmin.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_vssadmin.json @@ -31,5 +31,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_wmic.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_wmic.json index 94b8846741e3e8..bad3c65024e42c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_wmic.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_volume_shadow_copy_deletion_via_wmic.json @@ -31,5 +31,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_windows_script_executing_powershell.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_windows_script_executing_powershell.json index b0a754a662c0e0..52323b169cb223 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_windows_script_executing_powershell.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/eql_windows_script_executing_powershell.json @@ -31,5 +31,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_hping_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_hping_activity.json index bb8e8983661e6f..04a56241ea6f66 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_hping_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_hping_activity.json @@ -20,5 +20,5 @@ "Linux" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_iodine_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_iodine_activity.json index 4e49702855a764..80358cc775e3b4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_iodine_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_iodine_activity.json @@ -20,5 +20,5 @@ "Linux" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_kernel_module_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_kernel_module_activity.json index cf8cd72b7aa6f5..b50fcc4c9980bb 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_kernel_module_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_kernel_module_activity.json @@ -37,5 +37,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_mknod_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_mknod_activity.json index 3bd3848c07581c..d65440e95ff17a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_mknod_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_mknod_activity.json @@ -20,5 +20,5 @@ "Linux" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_netcat_network_connection.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_netcat_network_connection.json index cd523b6594ccd3..df8e46be7a1c3e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_netcat_network_connection.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_netcat_network_connection.json @@ -22,5 +22,5 @@ "Linux" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_nmap_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_nmap_activity.json index 604cfa172fd84d..2e5c899ebc6255 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_nmap_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_nmap_activity.json @@ -20,5 +20,5 @@ "Linux" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_nping_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_nping_activity.json index 8e71b5b9067114..168b30121c4bb3 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_nping_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_nping_activity.json @@ -20,5 +20,5 @@ "Linux" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_process_started_in_temp_directory.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_process_started_in_temp_directory.json index c50026d7736ae2..0865ac6c70cb26 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_process_started_in_temp_directory.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_process_started_in_temp_directory.json @@ -17,5 +17,5 @@ "Linux" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_shell_activity_by_web_server.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_shell_activity_by_web_server.json index 01f117e0a225bf..e9c4c95bb92849 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_shell_activity_by_web_server.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_shell_activity_by_web_server.json @@ -37,5 +37,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_socat_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_socat_activity.json index a16b164e9ee4a3..404fea63aff946 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_socat_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_socat_activity.json @@ -20,5 +20,5 @@ "Linux" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_strace_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_strace_activity.json index 9b18039b63fd0e..fbdfa9e66682d7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_strace_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_strace_activity.json @@ -20,5 +20,5 @@ "Linux" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_tcpdump_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_tcpdump_activity.json index 5ae48c8db9984c..82771074e7c29d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_tcpdump_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_tcpdump_activity.json @@ -49,5 +49,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_whoami_commmand.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_whoami_commmand.json index 7fef4e813da98f..7e7f041581eb07 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_whoami_commmand.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/linux_whoami_commmand.json @@ -34,5 +34,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/null_user_agent.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/null_user_agent.json index afbbb2a34d545d..01246de5595e93 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/null_user_agent.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/null_user_agent.json @@ -38,5 +38,5 @@ "Elastic" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/sqlmap_user_agent.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/sqlmap_user_agent.json index fd240262d021f0..10412c19da1b1e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/sqlmap_user_agent.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/sqlmap_user_agent.json @@ -20,5 +20,5 @@ "Elastic" ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_certutil_network_connection.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_certutil_network_connection.json index 2cda21cf7d5efc..52a373e3aeb77e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_certutil_network_connection.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_certutil_network_connection.json @@ -4,7 +4,6 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 33, "name": "Network Connection via Certutil", "query": "process.name:certutil.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)", "risk_score": 21, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_prompt_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_prompt_connecting_to_the_internet.json index 2427ab4d7cc559..2bee265a74e11c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_prompt_connecting_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_prompt_connecting_to_the_internet.json @@ -49,5 +49,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_powershell.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_powershell.json index f8e5bd22576a40..d8f91dba7dd890 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_powershell.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_powershell.json @@ -46,5 +46,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_svchost.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_svchost.json index 71aafa9984ecb1..6fd194ee2fa22b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_svchost.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_command_shell_started_by_svchost.json @@ -31,5 +31,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_msbuild.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_msbuild.json index 4ff78914385543..43050e2769a241 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_msbuild.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_credential_dumping_msbuild.json @@ -35,4 +35,4 @@ ], "type": "query", "version": 1 -} +} \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_cve_2020_0601.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_cve_2020_0601.json index c08bb7b3315f5d..f5eb37c70d268a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_cve_2020_0601.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_cve_2020_0601.json @@ -4,7 +4,6 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 33, "name": "Windows CryptoAPI Spoofing Vulnerability (CVE-2020-0601 - CurveBall)", "query": "event.provider:\"Microsoft-Windows-Audit-CVE\" and message:\"[CVE-2020-0601]\"", "risk_score": 21, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_via_filter_manager.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_via_filter_manager.json index 3f97f7aca74f60..0e8c5a5f2f631a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_via_filter_manager.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_defense_evasion_via_filter_manager.json @@ -31,5 +31,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_compiled_html_file.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_compiled_html_file.json index 2b6e1fb3daaec1..7755ff0233f7ce 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_compiled_html_file.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_compiled_html_file.json @@ -49,5 +49,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_net_com_assemblies.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_net_com_assemblies.json index c397c955fe64f0..d6acb81c10e3fa 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_net_com_assemblies.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_net_com_assemblies.json @@ -4,7 +4,6 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 33, "name": "Execution via Regsvcs/Regasm", "query": "process.name:(RegAsm.exe or RegSvcs.exe) and event.action:\"Process Create (rule: ProcessCreate)\"", "risk_score": 21, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_trusted_developer_utilities.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_trusted_developer_utilities.json index f60a986996d6ff..87e38febb07433 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_trusted_developer_utilities.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_execution_via_trusted_developer_utilities.json @@ -49,5 +49,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_html_help_executable_program_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_html_help_executable_program_connecting_to_the_internet.json index 4b3efead776d27..6c8cd0673256ac 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_html_help_executable_program_connecting_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_html_help_executable_program_connecting_to_the_internet.json @@ -46,5 +46,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_misc_lolbin_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_misc_lolbin_connecting_to_the_internet.json index 0cd68ba5c1ed88..a0e311d8eb1543 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_misc_lolbin_connecting_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_misc_lolbin_connecting_to_the_internet.json @@ -46,5 +46,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_modification_of_boot_config.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_modification_of_boot_config.json index d7612262764966..045a9789b12609 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_modification_of_boot_config.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_modification_of_boot_config.json @@ -4,7 +4,6 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 33, "name": "Modification of Boot Configuration", "query": "event.action:\"Process Create (rule: ProcessCreate)\" and process.name:bcdedit.exe and process.args:(/set and (bootstatuspolicy and ignoreallfailures or no and recoveryenabled))", "risk_score": 21, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_msxsl_network.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_msxsl_network.json index 9b45d03aae375c..e80dcde1e398db 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_msxsl_network.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_msxsl_network.json @@ -4,7 +4,6 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 33, "name": "Network Connection via MsXsl", "query": "process.name:msxsl.exe and event.action:\"Network connection detected (rule: NetworkConnect)\" and not destination.ip:(10.0.0.0/8 or 172.16.0.0/12 or 192.168.0.0/16)", "risk_score": 21, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_net_command_system_account.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_net_command_system_account.json index 390c9c278905c9..c2379142df002a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_net_command_system_account.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_net_command_system_account.json @@ -4,7 +4,6 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 33, "name": "Net command via SYSTEM account", "query": "(process.name:net.exe or process.name:net1.exe and not process.parent.name:net.exe) and user.name:SYSTEM and event.action:\"Process Create (rule: ProcessCreate)\"", "risk_score": 21, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_application_shimming.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_application_shimming.json index 0488667d06c821..2f44727f9e6f05 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_application_shimming.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_persistence_via_application_shimming.json @@ -46,5 +46,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_priv_escalation_via_accessibility_features.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_priv_escalation_via_accessibility_features.json index 26f0a0bcc245c8..aeff071ed4514e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_priv_escalation_via_accessibility_features.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_priv_escalation_via_accessibility_features.json @@ -46,5 +46,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_discovery_via_tasklist_command.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_discovery_via_tasklist_command.json index 28ebdb44fddd28..3a883fa51b763b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_discovery_via_tasklist_command.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_process_discovery_via_tasklist_command.json @@ -34,5 +34,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_register_server_program_connecting_to_the_internet.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_register_server_program_connecting_to_the_internet.json index 920ff28a9a9cdd..1e061f2ef9463c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_register_server_program_connecting_to_the_internet.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_register_server_program_connecting_to_the_internet.json @@ -49,5 +49,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_uac_bypass_event_viewer.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_uac_bypass_event_viewer.json index 0d4168640bc604..df7a6fe1285d1c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_uac_bypass_event_viewer.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_uac_bypass_event_viewer.json @@ -4,7 +4,6 @@ "winlogbeat-*" ], "language": "kuery", - "max_signals": 33, "name": "Bypass UAC via Event Viewer", "query": "process.parent.name:eventvwr.exe and event.action:\"Process Create (rule: ProcessCreate)\" and not process.executable:(\"C:\\Windows\\SysWOW64\\mmc.exe\" or \"C:\\Windows\\System32\\mmc.exe\")", "risk_score": 21, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_whoami_command_activity.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_whoami_command_activity.json index 46af0c5b586a50..93ce1f83dd64ec 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_whoami_command_activity.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/rules/prepackaged_rules/windows_whoami_command_activity.json @@ -34,5 +34,5 @@ } ], "type": "query", - "version": 1 + "version": 2 } \ No newline at end of file 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 index ac10143c1d8d07..e6ee1e6a297643 100644 --- 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 @@ -4,7 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertsClient, AlertServices } from '../../../../../../../plugins/alerting/server'; +import { + AlertsClient, + AlertServices, + PartialAlert, +} 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'; @@ -19,7 +23,7 @@ export const updateRuleActions = async ({ alertsClient, savedObjectsClient, ruleAlertId, -}: UpdateRuleActions) => { +}: UpdateRuleActions): Promise => { const rule = await readRules({ alertsClient, id: ruleAlertId }); if (rule == null) { return null; 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 f70c591243a765..bb66a5ee1342f7 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 @@ -9,13 +9,14 @@ import { AlertsClient, AlertServices } from '../../../../../../../plugins/alerti 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 +29,7 @@ export const updateRulesNotifications = async ({ enabled, name, throttle, -}: UpdateRulesNotifications) => { +}: UpdateRulesNotifications): Promise => { const ruleActions = await updateOrCreateRuleActionsSavedObject({ savedObjectsClient, ruleAlertId, 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 7804439ce07345..750c5574f4a729 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 8c86f4c85af1d8..4db8724db4e13d 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 f6856eec59966d..997d03369a699a 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 6704c9676fa562..66b198974f5743 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 6d7d7e93d7e6e0..7a211c5631da6f 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 f2c2b99bdac8c9..f1729e35ce1f07 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 e360ceaf02f4dc..e5183ed4df7bd9 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/search_after_bulk_create.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/search_after_bulk_create.test.ts index 06652028b37410..414270ffcdd5c8 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 @@ -34,7 +34,7 @@ describe('searchAfterAndBulkCreate', () => { test('if successful with empty search results', async () => { const sampleParams = sampleRuleAlertParams(); - const { success } = await searchAfterAndBulkCreate({ + const { success, createdSignalsCount } = await searchAfterAndBulkCreate({ someResult: sampleEmptyDocSearchResults(), ruleParams: sampleParams, services: mockService, @@ -57,6 +57,7 @@ describe('searchAfterAndBulkCreate', () => { }); expect(mockService.callCluster).toHaveBeenCalledTimes(0); expect(success).toEqual(true); + expect(createdSignalsCount).toEqual(0); }); test('if successful iteration of while loop with maxDocs', async () => { @@ -70,6 +71,11 @@ describe('searchAfterAndBulkCreate', () => { { fakeItemValue: 'fakeItemKey', }, + { + create: { + status: 201, + }, + }, ], }) .mockReturnValueOnce(repeatedSearchResultsWithSortId(3, 1, someGuids.slice(0, 3))) @@ -80,6 +86,11 @@ describe('searchAfterAndBulkCreate', () => { { fakeItemValue: 'fakeItemKey', }, + { + create: { + status: 201, + }, + }, ], }) .mockReturnValueOnce(repeatedSearchResultsWithSortId(3, 1, someGuids.slice(3, 6))) @@ -90,9 +101,14 @@ describe('searchAfterAndBulkCreate', () => { { fakeItemValue: 'fakeItemKey', }, + { + create: { + status: 201, + }, + }, ], }); - const { success } = await searchAfterAndBulkCreate({ + const { success, createdSignalsCount } = await searchAfterAndBulkCreate({ someResult: repeatedSearchResultsWithSortId(3, 1, someGuids.slice(6, 9)), ruleParams: sampleParams, services: mockService, @@ -115,13 +131,14 @@ describe('searchAfterAndBulkCreate', () => { }); expect(mockService.callCluster).toHaveBeenCalledTimes(5); expect(success).toEqual(true); + expect(createdSignalsCount).toEqual(3); }); test('if unsuccessful first bulk create', async () => { const someGuids = Array.from({ length: 4 }).map(x => uuid.v4()); const sampleParams = sampleRuleAlertParams(10); mockService.callCluster.mockReturnValue(sampleBulkCreateDuplicateResult); - const { success } = await searchAfterAndBulkCreate({ + const { success, createdSignalsCount } = await searchAfterAndBulkCreate({ someResult: repeatedSearchResultsWithSortId(4, 1, someGuids), ruleParams: sampleParams, services: mockService, @@ -144,6 +161,7 @@ describe('searchAfterAndBulkCreate', () => { }); expect(mockLogger.error).toHaveBeenCalled(); expect(success).toEqual(false); + expect(createdSignalsCount).toEqual(1); }); test('if unsuccessful iteration of searchAfterAndBulkCreate due to empty sort ids', async () => { @@ -155,9 +173,14 @@ describe('searchAfterAndBulkCreate', () => { { fakeItemValue: 'fakeItemKey', }, + { + create: { + status: 201, + }, + }, ], }); - const { success } = await searchAfterAndBulkCreate({ + const { success, createdSignalsCount } = await searchAfterAndBulkCreate({ someResult: sampleDocSearchResultsNoSortId(), ruleParams: sampleParams, services: mockService, @@ -180,6 +203,7 @@ describe('searchAfterAndBulkCreate', () => { }); expect(mockLogger.error).toHaveBeenCalled(); expect(success).toEqual(false); + expect(createdSignalsCount).toEqual(1); }); test('if unsuccessful iteration of searchAfterAndBulkCreate due to empty sort ids and 0 total hits', async () => { @@ -191,9 +215,14 @@ describe('searchAfterAndBulkCreate', () => { { fakeItemValue: 'fakeItemKey', }, + { + create: { + status: 201, + }, + }, ], }); - const { success } = await searchAfterAndBulkCreate({ + const { success, createdSignalsCount } = await searchAfterAndBulkCreate({ someResult: sampleDocSearchResultsNoSortIdNoHits(), ruleParams: sampleParams, services: mockService, @@ -215,6 +244,7 @@ describe('searchAfterAndBulkCreate', () => { throttle: 'no_actions', }); expect(success).toEqual(true); + expect(createdSignalsCount).toEqual(1); }); test('if successful iteration of while loop with maxDocs and search after returns results with no sort ids', async () => { @@ -228,10 +258,15 @@ describe('searchAfterAndBulkCreate', () => { { fakeItemValue: 'fakeItemKey', }, + { + create: { + status: 201, + }, + }, ], }) .mockReturnValueOnce(sampleDocSearchResultsNoSortId()); - const { success } = await searchAfterAndBulkCreate({ + const { success, createdSignalsCount } = await searchAfterAndBulkCreate({ someResult: repeatedSearchResultsWithSortId(4, 1, someGuids), ruleParams: sampleParams, services: mockService, @@ -253,6 +288,7 @@ describe('searchAfterAndBulkCreate', () => { throttle: 'no_actions', }); expect(success).toEqual(true); + expect(createdSignalsCount).toEqual(1); }); test('if successful iteration of while loop with maxDocs and search after returns empty results with no sort ids', async () => { @@ -266,10 +302,15 @@ describe('searchAfterAndBulkCreate', () => { { fakeItemValue: 'fakeItemKey', }, + { + create: { + status: 201, + }, + }, ], }) .mockReturnValueOnce(sampleEmptyDocSearchResults()); - const { success } = await searchAfterAndBulkCreate({ + const { success, createdSignalsCount } = await searchAfterAndBulkCreate({ someResult: repeatedSearchResultsWithSortId(4, 1, someGuids), ruleParams: sampleParams, services: mockService, @@ -291,6 +332,7 @@ describe('searchAfterAndBulkCreate', () => { throttle: 'no_actions', }); expect(success).toEqual(true); + expect(createdSignalsCount).toEqual(1); }); test('if returns false when singleSearchAfter throws an exception', async () => { @@ -304,12 +346,17 @@ describe('searchAfterAndBulkCreate', () => { { fakeItemValue: 'fakeItemKey', }, + { + create: { + status: 201, + }, + }, ], }) .mockImplementation(() => { throw Error('Fake Error'); }); - const { success } = await searchAfterAndBulkCreate({ + const { success, createdSignalsCount } = await searchAfterAndBulkCreate({ someResult: repeatedSearchResultsWithSortId(4, 1, someGuids), ruleParams: sampleParams, services: mockService, @@ -331,5 +378,6 @@ describe('searchAfterAndBulkCreate', () => { throttle: 'no_actions', }); expect(success).toEqual(false); + expect(createdSignalsCount).toEqual(1); }); }); 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 a5d5dd0a7b7109..ff81730bc4a72b 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 @@ -39,6 +39,7 @@ export interface SearchAfterAndBulkCreateReturnType { searchAfterTimes: string[]; bulkCreateTimes: string[]; lastLookBackDate: Date | null | undefined; + createdSignalsCount: number; } // search_after through documents and re-index using bulk endpoint. @@ -68,6 +69,7 @@ export const searchAfterAndBulkCreate = async ({ searchAfterTimes: [], bulkCreateTimes: [], lastLookBackDate: null, + createdSignalsCount: 0, }; if (someResult.hits.hits.length === 0) { toReturn.success = true; @@ -75,7 +77,7 @@ export const searchAfterAndBulkCreate = async ({ } logger.debug('[+] starting bulk insertion'); - const { bulkCreateDuration } = await singleBulkCreate({ + const { bulkCreateDuration, createdItemsCount } = await singleBulkCreate({ someResult, ruleParams, services, @@ -97,6 +99,9 @@ export const searchAfterAndBulkCreate = async ({ someResult.hits.hits.length > 0 ? new Date(someResult.hits.hits[someResult.hits.hits.length - 1]?._source['@timestamp']) : null; + if (createdItemsCount) { + toReturn.createdSignalsCount = createdItemsCount; + } if (bulkCreateDuration) { toReturn.bulkCreateTimes.push(bulkCreateDuration); } @@ -156,7 +161,10 @@ export const searchAfterAndBulkCreate = async ({ } sortId = sortIds[0]; logger.debug('next bulk index'); - const { bulkCreateDuration: bulkDuration } = await singleBulkCreate({ + const { + bulkCreateDuration: bulkDuration, + createdItemsCount: createdCount, + } = await singleBulkCreate({ someResult: searchResult, ruleParams, services, @@ -175,6 +183,7 @@ export const searchAfterAndBulkCreate = async ({ throttle, }); logger.debug('finished next bulk index'); + toReturn.createdSignalsCount += createdCount; if (bulkDuration) { toReturn.bulkCreateTimes.push(bulkDuration); } 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 new file mode 100644 index 00000000000000..3d6f443ce60d61 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals/signal_rule_alert_type.test.ts @@ -0,0 +1,399 @@ +/* + * 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'; +import { savedObjectsClientMock } from 'src/core/server/mocks'; +import { loggerMock } from 'src/core/server/logging/logger.mock'; +import { getResult, getMlResult } from '../routes/__mocks__/request_responses'; +import { signalRulesAlertType } from './signal_rule_alert_type'; +import { AlertInstance } from '../../../../../../../plugins/alerting/server'; +import { ruleStatusServiceFactory } from './rule_status_service'; +import { getGapBetweenRuns } from './utils'; +import { RuleExecutorOptions } from './types'; +import { searchAfterAndBulkCreate } from './search_after_bulk_create'; +import { scheduleNotificationActions } from '../notifications/schedule_notification_actions'; +import { RuleAlertType } from '../rules/types'; +import { findMlSignals } from './find_ml_signals'; +import { bulkCreateMlSignals } from './bulk_create_ml_signals'; + +jest.mock('./rule_status_saved_objects_client'); +jest.mock('./rule_status_service'); +jest.mock('./search_after_bulk_create'); +jest.mock('./get_filter'); +jest.mock('./utils'); +jest.mock('../notifications/schedule_notification_actions'); +jest.mock('./find_ml_signals'); +jest.mock('./bulk_create_ml_signals'); + +const getPayload = ( + ruleAlert: RuleAlertType, + alertInstanceFactoryMock: () => AlertInstance, + savedObjectsClient: ReturnType, + callClusterMock: jest.Mock +) => ({ + alertId: ruleAlert.id, + services: { + savedObjectsClient, + alertInstanceFactory: alertInstanceFactoryMock, + callCluster: callClusterMock, + }, + params: { + ...ruleAlert.params, + actions: [], + enabled: ruleAlert.enabled, + interval: ruleAlert.schedule.interval, + name: ruleAlert.name, + tags: ruleAlert.tags, + throttle: ruleAlert.throttle, + scrollSize: 10, + scrollLock: '0', + }, + state: {}, + spaceId: '', + name: 'name', + tags: [], + startedAt: new Date('2019-12-13T16:50:33.400Z'), + previousStartedAt: new Date('2019-12-13T16:40:33.400Z'), + createdBy: 'elastic', + updatedBy: 'elastic', +}); + +describe('rules_notification_alert_type', () => { + const version = '8.0.0'; + const jobsSummaryMock = jest.fn(); + const mlMock = { + mlClient: { + callAsInternalUser: jest.fn(), + close: jest.fn(), + asScoped: jest.fn(), + }, + jobServiceProvider: jest.fn().mockReturnValue({ + jobsSummary: jobsSummaryMock, + }), + anomalyDetectorsProvider: jest.fn(), + mlSystemProvider: jest.fn(), + modulesProvider: jest.fn(), + resultsServiceProvider: jest.fn(), + }; + let payload: RuleExecutorOptions; + let alert: ReturnType; + let alertInstanceMock: Record; + let alertInstanceFactoryMock: () => AlertInstance; + let savedObjectsClient: ReturnType; + let logger: ReturnType; + let callClusterMock: jest.Mock; + let ruleStatusService: Record; + + beforeEach(() => { + alertInstanceMock = { + scheduleActions: jest.fn(), + replaceState: jest.fn(), + }; + alertInstanceMock.replaceState.mockReturnValue(alertInstanceMock); + alertInstanceFactoryMock = jest.fn().mockReturnValue(alertInstanceMock); + callClusterMock = jest.fn(); + savedObjectsClient = savedObjectsClientMock.create(); + logger = loggerMock.create(); + ruleStatusService = { + success: jest.fn(), + find: jest.fn(), + goingToRun: jest.fn(), + error: jest.fn(), + }; + (ruleStatusServiceFactory as jest.Mock).mockReturnValue(ruleStatusService); + (getGapBetweenRuns as jest.Mock).mockReturnValue(moment.duration(0)); + (searchAfterAndBulkCreate as jest.Mock).mockResolvedValue({ + success: true, + searchAfterTimes: [], + createdSignalsCount: 10, + }); + callClusterMock.mockResolvedValue({ + hits: { + total: { value: 10 }, + }, + }); + const ruleAlert = getResult(); + savedObjectsClient.get.mockResolvedValue({ + id: 'id', + type: 'type', + references: [], + attributes: ruleAlert, + }); + + payload = getPayload(ruleAlert, alertInstanceFactoryMock, savedObjectsClient, callClusterMock); + + alert = signalRulesAlertType({ + logger, + version, + ml: mlMock, + }); + }); + + describe('executor', () => { + it('should warn about the gap between runs', async () => { + (getGapBetweenRuns as jest.Mock).mockReturnValue(moment.duration(1000)); + await alert.executor(payload); + expect(logger.warn).toHaveBeenCalled(); + expect(logger.warn.mock.calls[0][0]).toContain( + 'a few seconds (1000ms) has passed since last rule execution, and signals may have been missed.' + ); + expect(ruleStatusService.error).toHaveBeenCalled(); + expect(ruleStatusService.error.mock.calls[0][0]).toContain( + 'a few seconds (1000ms) has passed since last rule execution, and signals may have been missed.' + ); + expect(ruleStatusService.error.mock.calls[0][1]).toEqual({ + gap: 'a few seconds', + }); + }); + + it('should call scheduleActions if signalsCount was greater than 0 and rule has actions defined', 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(scheduleNotificationActions).toHaveBeenCalledWith( + expect.objectContaining({ + signalsCount: 10, + }) + ); + }); + + describe('ML rule', () => { + it('should throw an error if ML plugin was not available', async () => { + const ruleAlert = getMlResult(); + payload = getPayload( + ruleAlert, + alertInstanceFactoryMock, + savedObjectsClient, + callClusterMock + ); + alert = signalRulesAlertType({ + logger, + version, + ml: undefined, + }); + await alert.executor(payload); + expect(logger.error).toHaveBeenCalled(); + expect(logger.error.mock.calls[0][0]).toContain( + 'ML plugin unavailable during rule execution' + ); + }); + + it('should throw an error if machineLearningJobId or anomalyThreshold was not null', async () => { + const ruleAlert = getMlResult(); + ruleAlert.params.anomalyThreshold = undefined; + payload = getPayload( + ruleAlert, + alertInstanceFactoryMock, + savedObjectsClient, + callClusterMock + ); + await alert.executor(payload); + expect(logger.error).toHaveBeenCalled(); + expect(logger.error.mock.calls[0][0]).toContain( + 'Machine learning rule is missing job id and/or anomaly threshold' + ); + }); + + it('should throw an error if Machine learning job summary was null', async () => { + const ruleAlert = getMlResult(); + payload = getPayload( + ruleAlert, + alertInstanceFactoryMock, + savedObjectsClient, + callClusterMock + ); + jobsSummaryMock.mockResolvedValue([]); + await alert.executor(payload); + expect(logger.warn).toHaveBeenCalled(); + expect(logger.warn.mock.calls[0][0]).toContain('Machine learning job is not started'); + expect(ruleStatusService.error).toHaveBeenCalled(); + expect(ruleStatusService.error.mock.calls[0][0]).toContain( + 'Machine learning job is not started' + ); + }); + + it('should log an error if Machine learning job was not started', async () => { + const ruleAlert = getMlResult(); + payload = getPayload( + ruleAlert, + alertInstanceFactoryMock, + savedObjectsClient, + callClusterMock + ); + jobsSummaryMock.mockResolvedValue([ + { + id: 'some_job_id', + jobState: 'starting', + datafeedState: 'started', + }, + ]); + (findMlSignals as jest.Mock).mockResolvedValue({ + hits: { + hits: [], + }, + }); + await alert.executor(payload); + expect(logger.warn).toHaveBeenCalled(); + expect(logger.warn.mock.calls[0][0]).toContain('Machine learning job is not started'); + expect(ruleStatusService.error).toHaveBeenCalled(); + expect(ruleStatusService.error.mock.calls[0][0]).toContain( + 'Machine learning job is not started' + ); + }); + + it('should not call ruleStatusService.success if no anomalies were found', async () => { + const ruleAlert = getMlResult(); + payload = getPayload( + ruleAlert, + alertInstanceFactoryMock, + savedObjectsClient, + callClusterMock + ); + jobsSummaryMock.mockResolvedValue([]); + (findMlSignals as jest.Mock).mockResolvedValue({ + hits: { + hits: [], + }, + }); + (bulkCreateMlSignals as jest.Mock).mockResolvedValue({ + success: true, + bulkCreateDuration: 0, + createdItemsCount: 0, + }); + await alert.executor(payload); + expect(ruleStatusService.success).not.toHaveBeenCalled(); + }); + + it('should call ruleStatusService.success if signals were created', async () => { + const ruleAlert = getMlResult(); + payload = getPayload( + ruleAlert, + alertInstanceFactoryMock, + savedObjectsClient, + callClusterMock + ); + jobsSummaryMock.mockResolvedValue([ + { + id: 'some_job_id', + jobState: 'started', + datafeedState: 'started', + }, + ]); + (findMlSignals as jest.Mock).mockResolvedValue({ + hits: { + hits: [{}], + }, + }); + (bulkCreateMlSignals as jest.Mock).mockResolvedValue({ + success: true, + bulkCreateDuration: 1, + createdItemsCount: 1, + }); + await alert.executor(payload); + expect(ruleStatusService.success).toHaveBeenCalled(); + }); + + it('should call scheduleActions if signalsCount was greater than 0 and rule has actions defined', async () => { + const ruleAlert = getMlResult(); + 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', + }, + ]; + payload = getPayload( + ruleAlert, + alertInstanceFactoryMock, + savedObjectsClient, + callClusterMock + ); + savedObjectsClient.get.mockResolvedValue({ + id: 'id', + type: 'type', + references: [], + attributes: ruleAlert, + }); + jobsSummaryMock.mockResolvedValue([]); + (findMlSignals as jest.Mock).mockResolvedValue({ + hits: { + hits: [{}], + }, + }); + (bulkCreateMlSignals as jest.Mock).mockResolvedValue({ + success: true, + bulkCreateDuration: 1, + createdItemsCount: 1, + }); + + await alert.executor(payload); + + expect(scheduleNotificationActions).toHaveBeenCalledWith( + expect.objectContaining({ + signalsCount: 1, + }) + ); + }); + }); + }); + + describe('should catch error', () => { + it('when bulk indexing failed', async () => { + (searchAfterAndBulkCreate as jest.Mock).mockResolvedValue({ + success: false, + searchAfterTimes: [], + bulkCreateTimes: [], + lastLookBackDate: null, + createdSignalsCount: 0, + }); + await alert.executor(payload); + expect(logger.error).toHaveBeenCalled(); + expect(logger.error.mock.calls[0][0]).toContain( + 'Bulk Indexing of signals failed. Check logs for further details.' + ); + expect(ruleStatusService.error).toHaveBeenCalled(); + }); + + it('when error was thrown', async () => { + (searchAfterAndBulkCreate as jest.Mock).mockResolvedValue({}); + await alert.executor(payload); + expect(logger.error).toHaveBeenCalled(); + expect(logger.error.mock.calls[0][0]).toContain('An error occurred during rule execution'); + expect(ruleStatusService.error).toHaveBeenCalled(); + }); + + it('and call ruleStatusService with the default message', async () => { + (searchAfterAndBulkCreate as jest.Mock).mockRejectedValue({}); + await alert.executor(payload); + expect(logger.error).toHaveBeenCalled(); + expect(logger.error.mock.calls[0][0]).toContain('An error occurred during rule execution'); + expect(ruleStatusService.error).toHaveBeenCalled(); + }); + }); +}); 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 246701e94c99a2..faac4a547fc176 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 @@ -19,16 +19,19 @@ import { } from './search_after_bulk_create'; import { getFilter } from './get_filter'; import { SignalRuleAlertTypeDefinition, RuleAlertAttributes } from './types'; -import { getGapBetweenRuns, makeFloatString } from './utils'; +import { getGapBetweenRuns, makeFloatString, parseScheduleDates } from './utils'; 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 { getSignalsCount } from '../notifications/get_signals_count'; -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'; +import { getNotificationResultsLink } from '../notifications/utils'; export const signalRulesAlertType = ({ logger, @@ -71,6 +74,7 @@ export const signalRulesAlertType = ({ bulkCreateTimes: [], searchAfterTimes: [], lastLookBackDate: null, + createdSignalsCount: 0, }; const ruleStatusClient = ruleStatusSavedObjectsClientFactory(services.savedObjectsClient); const ruleStatusService = await ruleStatusServiceFactory({ @@ -161,7 +165,7 @@ export const signalRulesAlertType = ({ logger.info(buildRuleMessage(`Found ${anomalyCount} signals from ML anomalies.`)); } - const { success, bulkCreateDuration } = await bulkCreateMlSignals({ + const { success, bulkCreateDuration, createdItemsCount } = await bulkCreateMlSignals({ actions, throttle, someResult: anomalyResults, @@ -180,6 +184,7 @@ export const signalRulesAlertType = ({ tags, }); result.success = success; + result.createdSignalsCount = createdItemsCount; if (bulkCreateDuration) { result.bulkCreateTimes.push(bulkCreateDuration); } @@ -244,28 +249,31 @@ export const signalRulesAlertType = ({ if (result.success) { if (actions.length) { - const notificationRuleParams = { + const notificationRuleParams: NotificationRuleTypeParams = { ...ruleParams, name, id: savedObject.id, }; - const { signalsCount, resultsLink } = await getSignalsCount({ - from: `now-${interval}`, - to: 'now', - index: ruleParams.outputIndex, - ruleId: ruleParams.ruleId!, - kibanaSiemAppUrl: meta?.kibanaSiemAppUrl as string, - ruleAlertId: savedObject.id, - callCluster: services.callCluster, + + const fromInMs = parseScheduleDates(`now-${interval}`)?.format('x'); + const toInMs = parseScheduleDates('now')?.format('x'); + + const resultsLink = getNotificationResultsLink({ + from: fromInMs, + to: toInMs, + id: savedObject.id, + kibanaSiemAppUrl: meta?.kibana_siem_app_url, }); - logger.info(buildRuleMessage(`Found ${signalsCount} signals for notification.`)); + logger.info( + buildRuleMessage(`Found ${result.createdSignalsCount} signals for notification.`) + ); - if (signalsCount) { + if (result.createdSignalsCount) { const alertInstance = services.alertInstanceFactory(alertId); scheduleNotificationActions({ alertInstance, - signalsCount, + signalsCount: result.createdSignalsCount, resultsLink, ruleParams: notificationRuleParams, }); 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 45b5610e2d3c3f..56f061cdfa3ca9 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 @@ -144,7 +144,7 @@ describe('singleBulkCreate', () => { }, ], }); - const { success } = await singleBulkCreate({ + const { success, createdItemsCount } = await singleBulkCreate({ someResult: sampleDocSearchResultsNoSortId(), ruleParams: sampleParams, services: mockService, @@ -163,6 +163,7 @@ describe('singleBulkCreate', () => { throttle: 'no_actions', }); expect(success).toEqual(true); + expect(createdItemsCount).toEqual(0); }); test('create successful bulk create with docs with no versioning', async () => { @@ -176,7 +177,7 @@ describe('singleBulkCreate', () => { }, ], }); - const { success } = await singleBulkCreate({ + const { success, createdItemsCount } = await singleBulkCreate({ someResult: sampleDocSearchResultsNoSortIdNoVersion(), ruleParams: sampleParams, services: mockService, @@ -195,12 +196,13 @@ describe('singleBulkCreate', () => { throttle: 'no_actions', }); expect(success).toEqual(true); + expect(createdItemsCount).toEqual(0); }); test('create unsuccessful bulk create due to empty search results', async () => { const sampleParams = sampleRuleAlertParams(); mockService.callCluster.mockReturnValue(false); - const { success } = await singleBulkCreate({ + const { success, createdItemsCount } = await singleBulkCreate({ someResult: sampleEmptyDocSearchResults(), ruleParams: sampleParams, services: mockService, @@ -219,13 +221,14 @@ describe('singleBulkCreate', () => { throttle: 'no_actions', }); expect(success).toEqual(true); + expect(createdItemsCount).toEqual(0); }); test('create successful bulk create when bulk create has duplicate errors', async () => { const sampleParams = sampleRuleAlertParams(); const sampleSearchResult = sampleDocSearchResultsNoSortId; mockService.callCluster.mockReturnValue(sampleBulkCreateDuplicateResult); - const { success } = await singleBulkCreate({ + const { success, createdItemsCount } = await singleBulkCreate({ someResult: sampleSearchResult(), ruleParams: sampleParams, services: mockService, @@ -246,13 +249,14 @@ describe('singleBulkCreate', () => { expect(mockLogger.error).not.toHaveBeenCalled(); expect(success).toEqual(true); + expect(createdItemsCount).toEqual(1); }); test('create successful bulk create when bulk create has multiple error statuses', async () => { const sampleParams = sampleRuleAlertParams(); const sampleSearchResult = sampleDocSearchResultsNoSortId; mockService.callCluster.mockReturnValue(sampleBulkCreateErrorResult); - const { success } = await singleBulkCreate({ + const { success, createdItemsCount } = await singleBulkCreate({ someResult: sampleSearchResult(), ruleParams: sampleParams, services: mockService, @@ -273,6 +277,7 @@ describe('singleBulkCreate', () => { expect(mockLogger.error).toHaveBeenCalled(); expect(success).toEqual(true); + expect(createdItemsCount).toEqual(1); }); test('filter duplicate rules will return an empty array given an empty array', () => { @@ -341,4 +346,29 @@ describe('singleBulkCreate', () => { }, ]); }); + + test('create successful and returns proper createdItemsCount', async () => { + const sampleParams = sampleRuleAlertParams(); + mockService.callCluster.mockReturnValue(sampleBulkCreateDuplicateResult); + const { success, createdItemsCount } = await singleBulkCreate({ + someResult: sampleDocSearchResultsNoSortId(), + ruleParams: sampleParams, + services: mockService, + logger: mockLogger, + id: sampleRuleGuid, + signalsIndex: DEFAULT_SIGNALS_INDEX, + actions: [], + name: 'rule-name', + createdAt: '2020-01-28T15:58:34.810Z', + updatedAt: '2020-01-28T15:59:14.004Z', + createdBy: 'elastic', + updatedBy: 'elastic', + interval: '5m', + enabled: true, + tags: ['some fake tag 1', 'some fake tag 2'], + throttle: 'no_actions', + }); + expect(success).toEqual(true); + expect(createdItemsCount).toEqual(1); + }); }); 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 ffec40b839bf64..6dd8823b57e4de 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 @@ -58,6 +58,7 @@ export const filterDuplicateRules = ( export interface SingleBulkCreateResponse { success: boolean; bulkCreateDuration?: string; + createdItemsCount: number; } // Bulk Index documents. @@ -81,7 +82,7 @@ export const singleBulkCreate = async ({ }: SingleBulkCreateParams): Promise => { someResult.hits.hits = filterDuplicateRules(id, someResult); if (someResult.hits.hits.length === 0) { - return { success: true }; + return { success: true, createdItemsCount: 0 }; } // index documents after creating an ID based on the // source documents' originating index, and the original @@ -145,5 +146,8 @@ export const singleBulkCreate = async ({ ); } } - return { success: true, bulkCreateDuration: makeFloatString(end - start) }; + + const createdItemsCount = countBy(response.items, 'create.status')['201'] ?? 0; + + return { success: true, bulkCreateDuration: makeFloatString(end - start), createdItemsCount }; }; 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 543e8bf0619b00..d4469351de5442 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,5 @@ export interface AlertAttributes { } export interface RuleAlertAttributes extends AlertAttributes { - params: RuleAlertParams; + params: Omit & { 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 efa0a92cc573b5..d3fa98fd73d3a3 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; } 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 93472b8539efd7..20bc1387a3c4e7 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 48b9750ddd9499..7cfb5ad3911496 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 773d4ed6a7e953..76c865679dd058 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 34deee7fa88950..a439d105d63df1 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 a5877f6c34b8f2..3b8c92ebba2694 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 6002c9370210ea..0be2e48fe46689 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 53ed7c26b877f6..56ceca2b70e9ce 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 a191bd835a7c73..3e9be4e265e637 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 f34519da34ee8a..2b7be8f4b7539e 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 767a0fe72985e8..d4105519ee0a77 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/common/constants/rest_api.ts b/x-pack/legacy/plugins/uptime/common/constants/rest_api.ts index 86e2b03e13f22c..dffa131870db15 100644 --- a/x-pack/legacy/plugins/uptime/common/constants/rest_api.ts +++ b/x-pack/legacy/plugins/uptime/common/constants/rest_api.ts @@ -15,7 +15,7 @@ export enum API_URLS { PING_HISTOGRAM = `/api/uptime/ping/histogram`, SNAPSHOT_COUNT = `/api/uptime/snapshot/count`, FILTERS = `/api/uptime/filters`, - logPageView = `/api/uptime/logPageView`, + LOG_PAGE_VIEW = `/api/uptime/log_page_view`, ML_MODULE_JOBS = `/api/ml/modules/jobs_exist/`, ML_SETUP_MODULE = '/api/ml/modules/setup/', 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 b547f8b076f931..a49468ad3dd066 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 5143e1c9639048..b86e85f35b17d4 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/legacy/plugins/uptime/public/hooks/use_telemetry.ts b/x-pack/legacy/plugins/uptime/public/hooks/use_telemetry.ts index fc0e0ce1c3e882..13fe523332ae5c 100644 --- a/x-pack/legacy/plugins/uptime/public/hooks/use_telemetry.ts +++ b/x-pack/legacy/plugins/uptime/public/hooks/use_telemetry.ts @@ -30,6 +30,6 @@ export const useUptimeTelemetry = (page?: UptimePage) => { dateEnd: dateRangeEnd, autoRefreshEnabled: !autorefreshIsPaused, }; - apiService.post(API_URLS.logPageView, params); + apiService.post(API_URLS.LOG_PAGE_VIEW, params); }, [autorefreshInterval, autorefreshIsPaused, dateRangeEnd, dateRangeStart, page]); }; diff --git a/x-pack/legacy/plugins/uptime/public/uptime_app.tsx b/x-pack/legacy/plugins/uptime/public/uptime_app.tsx index fa2998532d145a..dafb20dc9c323d 100644 --- a/x-pack/legacy/plugins/uptime/public/uptime_app.tsx +++ b/x-pack/legacy/plugins/uptime/public/uptime_app.tsx @@ -104,7 +104,10 @@ const Application = (props: UptimeAppProps) => {
- +
diff --git a/x-pack/package.json b/x-pack/package.json index 24b23256bf18ea..b2ec4c3dc3f6f0 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -185,7 +185,7 @@ "@elastic/eui": "21.0.1", "@elastic/filesaver": "1.1.2", "@elastic/maki": "6.2.0", - "@elastic/node-crypto": "^1.0.0", + "@elastic/node-crypto": "1.1.1", "@elastic/numeral": "2.4.0", "@kbn/babel-preset": "1.0.0", "@kbn/config-schema": "1.0.0", diff --git a/x-pack/plugins/actions/README.md b/x-pack/plugins/actions/README.md index d217d26e84836b..82cc09f5e9eca6 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 f3042a701211f7..61b338d47b9f50 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 8a39d68f40bb6e..431bfb1e99c3bd 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 0df07ad58fb9e3..955e1569380a5d 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 129829850f9c1b..8f73bfb31ea4de 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/builtin_action_types/lib/send_email.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.ts index 74eead0708545f..47d7aff8022ce6 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.ts @@ -65,6 +65,11 @@ export async function sendEmail(logger: Logger, options: SendEmailOptions): Prom transportConfig.host = host; transportConfig.port = port; transportConfig.secure = !!secure; + if (!transportConfig.secure) { + transportConfig.tls = { + rejectUnauthorized: false, + }; + } } const nodemailerTransport = nodemailer.createTransport(transportConfig); diff --git a/x-pack/plugins/actions/server/config.test.ts b/x-pack/plugins/actions/server/config.test.ts index 67b7553c4a7361..51e87dbd75b48d 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 9e4795be6c2082..1f04efd1941b4b 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 eee2ae352fe3d0..88553c314112f3 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 00000000000000..884353e132b9c5 --- /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 75396f2aad897d..bc4268bb698723 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 383f84590fbc6d..6215b08df81d4f 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 ce31e62bc9b8e8..34c9e7aa9e8b8f 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 cddebb3a8e31e3..ffd1f0faabbabc 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 1b130421fa71fa..00000000000000 --- 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 45b967629a2a85..00000000000000 --- 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 00000000000000..6499427b8c1a53 --- /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 00000000000000..c70a13bc01c9f8 --- /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 33191132bece55..94f9ec1c943646 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 999e739e77060c..92e38d77314f83 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 9d4ea69a636090..2574e73dd4f9ae 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 0e929ff457fbd2..a9ff5ee8ecdc62 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 5538b44b69fcbb..6f8478df58a533 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 4c74ca54a0d2f4..951d18a33b35f1 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 fd480658e236ac..734417e72733e3 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 90e274df3a5ee8..fdca6c0a9b503c 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 9efe020bc10c4b..42c83a7c04deba 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 29c2f3c5730f4c..fa2358a1f181ce 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/agent_configuration/amount_and_unit.ts b/x-pack/plugins/apm/common/agent_configuration/amount_and_unit.ts index 447e529c9c199a..d6520ae1505398 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 a83ee9262cad67..98d0cb5f028c33 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 383fd69be9a78e..b691276854fb0a 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 4b74b07fc8e27d..49840d2157af76 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 152db37a1bff32..e73aed35e87f94 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 bb050076b9f9a9..2e10c743785494 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 6b584fc7e20486..282ced346dda05 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/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 0fe825e8ace359..d7e28828572d55 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 9b210c2aa05adc..d92af587d0e92b 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 88c576c70bdf0e..a8a42fe11e7ceb 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 301f184af7d811..6b329bccab4a73 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/encrypted_saved_objects/README.md b/x-pack/plugins/encrypted_saved_objects/README.md index a352989870079e..6085b52d392a4d 100644 --- a/x-pack/plugins/encrypted_saved_objects/README.md +++ b/x-pack/plugins/encrypted_saved_objects/README.md @@ -100,10 +100,10 @@ $ node scripts/jest.js In one shell, from `kibana-root-folder/x-pack`: ```bash -$ node scripts/functional_tests_server.js --config test/plugin_api_integration/config.js +$ node scripts/functional_tests_server.js --config test/encrypted_saved_objects_api_integration/config.ts ``` In another shell, from `kibana-root-folder/x-pack`: ```bash -$ node ../scripts/functional_test_runner.js --config test/plugin_api_integration/config.js --grep="{TEST_NAME}" +$ node ../scripts/functional_test_runner.js --config test/encrypted_saved_objects_api_integration/config.ts --grep="{TEST_NAME}" ``` diff --git a/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.test.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.test.ts index e1e1a8224aa7b5..be33238a26ee70 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.test.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.test.ts @@ -19,9 +19,10 @@ beforeEach(() => { mockAuditLogger = encryptedSavedObjectsAuditLoggerMock.create(); // Call actual `@elastic/node-crypto` by default, but allow to override implementation in tests. - jest - .requireMock('@elastic/node-crypto') - .mockImplementation((...args: any[]) => jest.requireActual('@elastic/node-crypto')(...args)); + jest.requireMock('@elastic/node-crypto').mockImplementation((...args: any[]) => { + const { default: nodeCrypto } = jest.requireActual('@elastic/node-crypto'); + return nodeCrypto(...args); + }); service = new EncryptedSavedObjectsService( 'encryption-key-abc', diff --git a/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.ts index 94c16845295774..eea2b12354d9b7 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.ts @@ -4,8 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -// @ts-ignore -import nodeCrypto from '@elastic/node-crypto'; +import nodeCrypto, { Crypto } from '@elastic/node-crypto'; import stringify from 'json-stable-stringify'; import typeDetect from 'type-detect'; import { Logger } from 'src/core/server'; @@ -49,10 +48,7 @@ export function descriptorToArray(descriptor: SavedObjectDescriptor) { * attributes. */ export class EncryptedSavedObjectsService { - private readonly crypto: Readonly<{ - encrypt(valueToEncrypt: T, aad?: string): Promise; - decrypt(valueToDecrypt: string, aad?: string): Promise; - }>; + private readonly crypto: Readonly; /** * Map of all registered saved object types where the `key` is saved object type and the `value` @@ -229,10 +225,10 @@ export class EncryptedSavedObjectsService { } try { - decryptedAttributes[attributeName] = await this.crypto.decrypt( + decryptedAttributes[attributeName] = (await this.crypto.decrypt( attributeValue, encryptionAAD - ); + )) as string; } catch (err) { this.logger.error(`Failed to decrypt "${attributeName}" attribute: ${err.message || err}`); this.audit.decryptAttributeFailure(attributeName, descriptor); diff --git a/x-pack/plugins/endpoint/common/generate_data.ts b/x-pack/plugins/endpoint/common/generate_data.ts index 0ec105129b7ac4..7c24bd9d771483 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 e8e1281a889253..a614526d92a3f9 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 fa9055e0d9bbd3..89a6302351a546 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 00000000000000..af34205e2310f3 --- /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 e1a90b4a416dc2..00cf0bca57e66f 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 00000000000000..65e78f27943ba0 --- /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 dee35aa3b895a2..4dafa68ddb647d 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 9aff66cdfb75ef..6148934343635a 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 a1973a38b65347..8c8578426aa294 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 db39ecf4483122..d4c2602e34387e 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 fd70317a9f37ee..ad6741dab7be7f 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 af9b49cea18adc..fb3e26157ef32a 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 00000000000000..ca27831ee90b51 --- /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 d0751cf9fb886e..a3e30eb891db43 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 00000000000000..2ecc2b117bf017 --- /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 2dba301bf45376..bc56e5e6f63290 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', @@ -101,7 +98,7 @@ export async function getHostData( return undefined; } - return response.hits.hits[0]._source; + return enrichHostMetadata(response.hits.hits[0]._source); } function mapToHostResultList( @@ -116,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 { @@ -128,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.test.ts b/x-pack/plugins/endpoint/server/routes/metadata/metadata.test.ts similarity index 94% rename from x-pack/plugins/endpoint/server/routes/metadata.test.ts rename to x-pack/plugins/endpoint/server/routes/metadata/metadata.test.ts index 65e07edbcde249..9bd251735cc044 100644 --- a/x-pack/plugins/endpoint/server/routes/metadata.test.ts +++ b/x-pack/plugins/endpoint/server/routes/metadata/metadata.test.ts @@ -17,12 +17,12 @@ import { httpServerMock, httpServiceMock, loggingServiceMock, -} from '../../../../../src/core/server/mocks'; -import { HostMetadata, HostResultList } from '../../common/types'; +} from '../../../../../../src/core/server/mocks'; +import { HostInfo, HostMetadata, HostResultList, HostStatus } from '../../../common/types'; import { SearchResponse } from 'elasticsearch'; -import { registerEndpointRoutes } from './metadata'; -import { EndpointConfigSchema } from '../config'; -import * as data from '../test_data/all_metadata_data.json'; +import { EndpointConfigSchema } from '../../config'; +import * as data from '../../test_data/all_metadata_data.json'; +import { registerEndpointRoutes } from './index'; describe('test endpoint route', () => { let routerMock: jest.Mocked; @@ -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/endpoint/server/services/endpoint/metadata_query_builders.test.ts b/x-pack/plugins/endpoint/server/routes/metadata/query_builders.test.ts similarity index 97% rename from x-pack/plugins/endpoint/server/services/endpoint/metadata_query_builders.test.ts rename to x-pack/plugins/endpoint/server/routes/metadata/query_builders.test.ts index 0966b52c79f7d0..2514d5aa858111 100644 --- a/x-pack/plugins/endpoint/server/services/endpoint/metadata_query_builders.test.ts +++ b/x-pack/plugins/endpoint/server/routes/metadata/query_builders.test.ts @@ -5,10 +5,7 @@ */ import { httpServerMock, loggingServiceMock } from '../../../../../../src/core/server/mocks'; import { EndpointConfigSchema } from '../../config'; -import { - kibanaRequestToMetadataListESQuery, - getESQueryHostMetadataByID, -} from './metadata_query_builders'; +import { kibanaRequestToMetadataListESQuery, getESQueryHostMetadataByID } from './query_builders'; import { EndpointAppConstants } from '../../../common/types'; describe('query builder', () => { diff --git a/x-pack/plugins/endpoint/server/services/endpoint/metadata_query_builders.ts b/x-pack/plugins/endpoint/server/routes/metadata/query_builders.ts similarity index 100% rename from x-pack/plugins/endpoint/server/services/endpoint/metadata_query_builders.ts rename to x-pack/plugins/endpoint/server/routes/metadata/query_builders.ts index 57b0a4ef105194..bd07604fe9ad24 100644 --- a/x-pack/plugins/endpoint/server/services/endpoint/metadata_query_builders.ts +++ b/x-pack/plugins/endpoint/server/routes/metadata/query_builders.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ import { KibanaRequest } from 'kibana/server'; -import { EndpointAppConstants } from '../../../common/types'; -import { EndpointAppContext } from '../../types'; import { esKuery } from '../../../../../../src/plugins/data/server'; +import { EndpointAppContext } from '../../types'; +import { EndpointAppConstants } from '../../../common/types'; export const kibanaRequestToMetadataListESQuery = async ( request: KibanaRequest, diff --git a/x-pack/plugins/event_log/common/index.ts b/x-pack/plugins/event_log/common/index.ts new file mode 100644 index 00000000000000..3ee274916c127c --- /dev/null +++ b/x-pack/plugins/event_log/common/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const BASE_EVENT_LOG_API_PATH = '/api/event_log'; diff --git a/x-pack/plugins/event_log/server/es/cluster_client_adapter.mock.ts b/x-pack/plugins/event_log/server/es/cluster_client_adapter.mock.ts index 87e8fb0f521a9e..bd57958b0cb88a 100644 --- a/x-pack/plugins/event_log/server/es/cluster_client_adapter.mock.ts +++ b/x-pack/plugins/event_log/server/es/cluster_client_adapter.mock.ts @@ -15,6 +15,7 @@ const createClusterClientMock = () => { createIndexTemplate: jest.fn(), doesAliasExist: jest.fn(), createIndex: jest.fn(), + queryEventsBySavedObject: jest.fn(), }; return mock; }; diff --git a/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts b/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts index b61196439ee4fa..ae26d7a7ece07d 100644 --- a/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts +++ b/x-pack/plugins/event_log/server/es/cluster_client_adapter.test.ts @@ -7,6 +7,8 @@ import { ClusterClient, Logger } from '../../../../../src/core/server'; import { elasticsearchServiceMock, loggingServiceMock } from '../../../../../src/core/server/mocks'; import { ClusterClientAdapter, IClusterClientAdapter } from './cluster_client_adapter'; +import moment from 'moment'; +import { findOptionsSchema } from '../event_log_client'; type EsClusterClient = Pick, 'callAsInternalUser' | 'asScoped'>; @@ -195,3 +197,230 @@ describe('createIndex', () => { await clusterClientAdapter.createIndex('foo'); }); }); + +describe('queryEventsBySavedObject', () => { + const DEFAULT_OPTIONS = findOptionsSchema.validate({}); + + test('should call cluster with proper arguments', async () => { + clusterClient.callAsInternalUser.mockResolvedValue({ + hits: { + hits: [], + total: { value: 0 }, + }, + }); + await clusterClientAdapter.queryEventsBySavedObject( + 'index-name', + 'saved-object-type', + 'saved-object-id', + DEFAULT_OPTIONS + ); + + const [method, query] = clusterClient.callAsInternalUser.mock.calls[0]; + expect(method).toEqual('search'); + expect(query).toMatchObject({ + index: 'index-name', + body: { + from: 0, + size: 10, + sort: { 'event.start': { order: 'asc' } }, + query: { + bool: { + must: [ + { + nested: { + path: 'kibana.saved_objects', + query: { + bool: { + must: [ + { + term: { + 'kibana.saved_objects.type': { + value: 'saved-object-type', + }, + }, + }, + { + term: { + 'kibana.saved_objects.id': { + value: 'saved-object-id', + }, + }, + }, + ], + }, + }, + }, + }, + ], + }, + }, + }, + }); + }); + + test('should call cluster with sort', async () => { + clusterClient.callAsInternalUser.mockResolvedValue({ + hits: { + hits: [], + total: { value: 0 }, + }, + }); + await clusterClientAdapter.queryEventsBySavedObject( + 'index-name', + 'saved-object-type', + 'saved-object-id', + { ...DEFAULT_OPTIONS, sort_field: 'event.end', sort_order: 'desc' } + ); + + const [method, query] = clusterClient.callAsInternalUser.mock.calls[0]; + expect(method).toEqual('search'); + expect(query).toMatchObject({ + index: 'index-name', + body: { + sort: { 'event.end': { order: 'desc' } }, + }, + }); + }); + + test('supports open ended date', async () => { + clusterClient.callAsInternalUser.mockResolvedValue({ + hits: { + hits: [], + total: { value: 0 }, + }, + }); + + const start = moment() + .subtract(1, 'days') + .toISOString(); + + await clusterClientAdapter.queryEventsBySavedObject( + 'index-name', + 'saved-object-type', + 'saved-object-id', + { ...DEFAULT_OPTIONS, start } + ); + + const [method, query] = clusterClient.callAsInternalUser.mock.calls[0]; + expect(method).toEqual('search'); + expect(query).toMatchObject({ + index: 'index-name', + body: { + query: { + bool: { + must: [ + { + nested: { + path: 'kibana.saved_objects', + query: { + bool: { + must: [ + { + term: { + 'kibana.saved_objects.type': { + value: 'saved-object-type', + }, + }, + }, + { + term: { + 'kibana.saved_objects.id': { + value: 'saved-object-id', + }, + }, + }, + ], + }, + }, + }, + }, + { + range: { + 'event.start': { + gte: start, + }, + }, + }, + ], + }, + }, + }, + }); + }); + + test('supports optional date range', async () => { + clusterClient.callAsInternalUser.mockResolvedValue({ + hits: { + hits: [], + total: { value: 0 }, + }, + }); + + const start = moment() + .subtract(1, 'days') + .toISOString(); + const end = moment() + .add(1, 'days') + .toISOString(); + + await clusterClientAdapter.queryEventsBySavedObject( + 'index-name', + 'saved-object-type', + 'saved-object-id', + { ...DEFAULT_OPTIONS, start, end } + ); + + const [method, query] = clusterClient.callAsInternalUser.mock.calls[0]; + expect(method).toEqual('search'); + expect(query).toMatchObject({ + index: 'index-name', + body: { + query: { + bool: { + must: [ + { + nested: { + path: 'kibana.saved_objects', + query: { + bool: { + must: [ + { + term: { + 'kibana.saved_objects.type': { + value: 'saved-object-type', + }, + }, + }, + { + term: { + 'kibana.saved_objects.id': { + value: 'saved-object-id', + }, + }, + }, + ], + }, + }, + }, + }, + { + range: { + 'event.start': { + gte: start, + }, + }, + }, + { + range: { + 'event.end': { + lte: end, + }, + }, + }, + ], + }, + }, + }, + }); + }); +}); diff --git a/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts b/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts index d585fd4f539b5c..36bc94edfca4ee 100644 --- a/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts +++ b/x-pack/plugins/event_log/server/es/cluster_client_adapter.ts @@ -4,7 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import { reject, isUndefined } from 'lodash'; import { Logger, ClusterClient } from '../../../../../src/core/server'; +import { IEvent } from '../types'; +import { FindOptionsType } from '../event_log_client'; export type EsClusterClient = Pick; export type IClusterClientAdapter = PublicMethodsOf; @@ -14,6 +17,13 @@ export interface ConstructorOpts { clusterClient: EsClusterClient; } +export interface QueryEventsBySavedObjectResult { + page: number; + per_page: number; + total: number; + data: IEvent[]; +} + export class ClusterClientAdapter { private readonly logger: Logger; private readonly clusterClient: EsClusterClient; @@ -107,6 +117,87 @@ export class ClusterClientAdapter { } } + public async queryEventsBySavedObject( + index: string, + type: string, + id: string, + { page, per_page: perPage, start, end, sort_field, sort_order }: FindOptionsType + ): Promise { + try { + const { + hits: { + hits, + total: { value: total }, + }, + } = await this.callEs('search', { + index, + body: { + size: perPage, + from: (page - 1) * perPage, + sort: { [sort_field]: { order: sort_order } }, + query: { + bool: { + must: reject( + [ + { + nested: { + path: 'kibana.saved_objects', + query: { + bool: { + must: [ + { + term: { + 'kibana.saved_objects.type': { + value: type, + }, + }, + }, + { + term: { + 'kibana.saved_objects.id': { + value: id, + }, + }, + }, + ], + }, + }, + }, + }, + start && { + range: { + 'event.start': { + gte: start, + }, + }, + }, + end && { + range: { + 'event.end': { + lte: end, + }, + }, + }, + ], + isUndefined + ), + }, + }, + }, + }); + return { + page, + per_page: perPage, + total, + data: hits.map((hit: any) => hit._source) as IEvent[], + }; + } catch (err) { + throw new Error( + `querying for Event Log by for type "${type}" and id "${id}" failed with: ${err.message}` + ); + } + } + private async callEs(operation: string, body?: any): Promise { try { this.debug(`callEs(${operation}) calls:`, body); diff --git a/x-pack/plugins/event_log/server/event_log_client.mock.ts b/x-pack/plugins/event_log/server/event_log_client.mock.ts new file mode 100644 index 00000000000000..31cab802555d06 --- /dev/null +++ b/x-pack/plugins/event_log/server/event_log_client.mock.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IEventLogClient } from './types'; + +const createEventLogClientMock = () => { + const mock: jest.Mocked = { + findEventsBySavedObject: jest.fn(), + }; + return mock; +}; + +export const eventLogClientMock = { + create: createEventLogClientMock, +}; diff --git a/x-pack/plugins/event_log/server/event_log_client.test.ts b/x-pack/plugins/event_log/server/event_log_client.test.ts new file mode 100644 index 00000000000000..6d4c9b67abc1ba --- /dev/null +++ b/x-pack/plugins/event_log/server/event_log_client.test.ts @@ -0,0 +1,292 @@ +/* + * 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 { EventLogClient } from './event_log_client'; +import { contextMock } from './es/context.mock'; +import { savedObjectsClientMock } from 'src/core/server/mocks'; +import { merge } from 'lodash'; +import moment from 'moment'; + +describe('EventLogStart', () => { + describe('findEventsBySavedObject', () => { + test('verifies that the user can access the specified saved object', async () => { + const esContext = contextMock.create(); + const savedObjectsClient = savedObjectsClientMock.create(); + const eventLogClient = new EventLogClient({ + esContext, + savedObjectsClient, + }); + + savedObjectsClient.get.mockResolvedValueOnce({ + id: 'saved-object-id', + type: 'saved-object-type', + attributes: {}, + references: [], + }); + + await eventLogClient.findEventsBySavedObject('saved-object-type', 'saved-object-id'); + + expect(savedObjectsClient.get).toHaveBeenCalledWith('saved-object-type', 'saved-object-id'); + }); + + test('throws when the user doesnt have permission to access the specified saved object', async () => { + const esContext = contextMock.create(); + const savedObjectsClient = savedObjectsClientMock.create(); + const eventLogClient = new EventLogClient({ + esContext, + savedObjectsClient, + }); + + savedObjectsClient.get.mockRejectedValue(new Error('Fail')); + + expect( + eventLogClient.findEventsBySavedObject('saved-object-type', 'saved-object-id') + ).rejects.toMatchInlineSnapshot(`[Error: Fail]`); + }); + + test('fetches all event that reference the saved object', async () => { + const esContext = contextMock.create(); + const savedObjectsClient = savedObjectsClientMock.create(); + const eventLogClient = new EventLogClient({ + esContext, + savedObjectsClient, + }); + + savedObjectsClient.get.mockResolvedValueOnce({ + id: 'saved-object-id', + type: 'saved-object-type', + attributes: {}, + references: [], + }); + + const expectedEvents = [ + fakeEvent({ + kibana: { + saved_objects: [ + { + id: 'saved-object-id', + type: 'saved-object-type', + }, + { + type: 'action', + id: '1', + }, + ], + }, + }), + fakeEvent({ + kibana: { + saved_objects: [ + { + id: 'saved-object-id', + type: 'saved-object-type', + }, + { + type: 'action', + id: '2', + }, + ], + }, + }), + ]; + + const result = { + page: 0, + per_page: 10, + total: expectedEvents.length, + data: expectedEvents, + }; + esContext.esAdapter.queryEventsBySavedObject.mockResolvedValue(result); + + expect( + await eventLogClient.findEventsBySavedObject('saved-object-type', 'saved-object-id') + ).toEqual(result); + + expect(esContext.esAdapter.queryEventsBySavedObject).toHaveBeenCalledWith( + esContext.esNames.alias, + 'saved-object-type', + 'saved-object-id', + { + page: 1, + per_page: 10, + sort_field: 'event.start', + sort_order: 'asc', + } + ); + }); + + test('fetches all events in time frame that reference the saved object', async () => { + const esContext = contextMock.create(); + const savedObjectsClient = savedObjectsClientMock.create(); + const eventLogClient = new EventLogClient({ + esContext, + savedObjectsClient, + }); + + savedObjectsClient.get.mockResolvedValueOnce({ + id: 'saved-object-id', + type: 'saved-object-type', + attributes: {}, + references: [], + }); + + const expectedEvents = [ + fakeEvent({ + kibana: { + saved_objects: [ + { + id: 'saved-object-id', + type: 'saved-object-type', + }, + { + type: 'action', + id: '1', + }, + ], + }, + }), + fakeEvent({ + kibana: { + saved_objects: [ + { + id: 'saved-object-id', + type: 'saved-object-type', + }, + { + type: 'action', + id: '2', + }, + ], + }, + }), + ]; + + const result = { + page: 0, + per_page: 10, + total: expectedEvents.length, + data: expectedEvents, + }; + esContext.esAdapter.queryEventsBySavedObject.mockResolvedValue(result); + + const start = moment() + .subtract(1, 'days') + .toISOString(); + const end = moment() + .add(1, 'days') + .toISOString(); + + expect( + await eventLogClient.findEventsBySavedObject('saved-object-type', 'saved-object-id', { + start, + end, + }) + ).toEqual(result); + + expect(esContext.esAdapter.queryEventsBySavedObject).toHaveBeenCalledWith( + esContext.esNames.alias, + 'saved-object-type', + 'saved-object-id', + { + page: 1, + per_page: 10, + sort_field: 'event.start', + sort_order: 'asc', + start, + end, + } + ); + }); + + test('validates that the start date is valid', async () => { + const esContext = contextMock.create(); + const savedObjectsClient = savedObjectsClientMock.create(); + const eventLogClient = new EventLogClient({ + esContext, + savedObjectsClient, + }); + + savedObjectsClient.get.mockResolvedValueOnce({ + id: 'saved-object-id', + type: 'saved-object-type', + attributes: {}, + references: [], + }); + + esContext.esAdapter.queryEventsBySavedObject.mockResolvedValue({ + page: 0, + per_page: 0, + total: 0, + data: [], + }); + + expect( + eventLogClient.findEventsBySavedObject('saved-object-type', 'saved-object-id', { + start: 'not a date string', + }) + ).rejects.toMatchInlineSnapshot(`[Error: [start]: Invalid Date]`); + }); + + test('validates that the end date is valid', async () => { + const esContext = contextMock.create(); + const savedObjectsClient = savedObjectsClientMock.create(); + const eventLogClient = new EventLogClient({ + esContext, + savedObjectsClient, + }); + + savedObjectsClient.get.mockResolvedValueOnce({ + id: 'saved-object-id', + type: 'saved-object-type', + attributes: {}, + references: [], + }); + + esContext.esAdapter.queryEventsBySavedObject.mockResolvedValue({ + page: 0, + per_page: 0, + total: 0, + data: [], + }); + + expect( + eventLogClient.findEventsBySavedObject('saved-object-type', 'saved-object-id', { + end: 'not a date string', + }) + ).rejects.toMatchInlineSnapshot(`[Error: [end]: Invalid Date]`); + }); + }); +}); + +function fakeEvent(overrides = {}) { + return merge( + { + event: { + provider: 'actions', + action: 'execute', + start: '2020-03-30T14:55:47.054Z', + end: '2020-03-30T14:55:47.055Z', + duration: 1000000, + }, + kibana: { + namespace: 'default', + saved_objects: [ + { + type: 'action', + id: '968f1b82-0414-4a10-becc-56b6473e4a29', + }, + ], + server_uuid: '5b2de169-2785-441b-ae8c-186a1936b17d', + }, + message: 'action executed: .server-log:968f1b82-0414-4a10-becc-56b6473e4a29: logger', + '@timestamp': '2020-03-30T14:55:47.055Z', + ecs: { + version: '1.3.1', + }, + }, + overrides + ); +} diff --git a/x-pack/plugins/event_log/server/event_log_client.ts b/x-pack/plugins/event_log/server/event_log_client.ts new file mode 100644 index 00000000000000..765f0895f8e0d1 --- /dev/null +++ b/x-pack/plugins/event_log/server/event_log_client.ts @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Observable } from 'rxjs'; +import { ClusterClient, SavedObjectsClientContract } from 'src/core/server'; + +import { schema, TypeOf } from '@kbn/config-schema'; +import { EsContext } from './es'; +import { IEventLogClient } from './types'; +import { QueryEventsBySavedObjectResult } from './es/cluster_client_adapter'; +export type PluginClusterClient = Pick; +export type AdminClusterClient$ = Observable; + +interface EventLogServiceCtorParams { + esContext: EsContext; + savedObjectsClient: SavedObjectsClientContract; +} + +const optionalDateFieldSchema = schema.maybe( + schema.string({ + validate(value) { + if (isNaN(Date.parse(value))) { + return 'Invalid Date'; + } + }, + }) +); + +export const findOptionsSchema = schema.object({ + per_page: schema.number({ defaultValue: 10, min: 0 }), + page: schema.number({ defaultValue: 1, min: 1 }), + start: optionalDateFieldSchema, + end: optionalDateFieldSchema, + sort_field: schema.oneOf( + [ + schema.literal('event.start'), + schema.literal('event.end'), + schema.literal('event.provider'), + schema.literal('event.duration'), + schema.literal('event.action'), + schema.literal('message'), + ], + { + defaultValue: 'event.start', + } + ), + sort_order: schema.oneOf([schema.literal('asc'), schema.literal('desc')], { + defaultValue: 'asc', + }), +}); +// page & perPage are required, other fields are optional +// using schema.maybe allows us to set undefined, but not to make the field optional +export type FindOptionsType = Pick< + TypeOf, + 'page' | 'per_page' | 'sort_field' | 'sort_order' +> & + Partial>; + +// note that clusterClient may be null, indicating we can't write to ES +export class EventLogClient implements IEventLogClient { + private esContext: EsContext; + private savedObjectsClient: SavedObjectsClientContract; + + constructor({ esContext, savedObjectsClient }: EventLogServiceCtorParams) { + this.esContext = esContext; + this.savedObjectsClient = savedObjectsClient; + } + + async findEventsBySavedObject( + type: string, + id: string, + options?: Partial + ): Promise { + // verify the user has the required permissions to view this saved object + await this.savedObjectsClient.get(type, id); + return await this.esContext.esAdapter.queryEventsBySavedObject( + this.esContext.esNames.alias, + type, + id, + findOptionsSchema.validate(options ?? {}) + ); + } +} diff --git a/x-pack/plugins/event_log/server/event_log_start_service.mock.ts b/x-pack/plugins/event_log/server/event_log_start_service.mock.ts new file mode 100644 index 00000000000000..e99ec777b473b1 --- /dev/null +++ b/x-pack/plugins/event_log/server/event_log_start_service.mock.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IEventLogClientService } from './types'; + +const createEventLogServiceMock = () => { + const mock: jest.Mocked = { + getClient: jest.fn(), + }; + return mock; +}; + +export const eventLogStartServiceMock = { + create: createEventLogServiceMock, +}; diff --git a/x-pack/plugins/event_log/server/event_log_start_service.test.ts b/x-pack/plugins/event_log/server/event_log_start_service.test.ts new file mode 100644 index 00000000000000..a8d75bc6c2e5a2 --- /dev/null +++ b/x-pack/plugins/event_log/server/event_log_start_service.test.ts @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EventLogClientService } from './event_log_start_service'; +import { contextMock } from './es/context.mock'; +import { KibanaRequest } from 'kibana/server'; +import { savedObjectsServiceMock } from 'src/core/server/saved_objects/saved_objects_service.mock'; +import { savedObjectsClientMock } from 'src/core/server/mocks'; + +jest.mock('./event_log_client'); + +describe('EventLogClientService', () => { + const esContext = contextMock.create(); + + describe('getClient', () => { + test('creates a client with a scoped SavedObjects client', () => { + const savedObjectsService = savedObjectsServiceMock.createStartContract(); + const request = fakeRequest(); + + const eventLogStartService = new EventLogClientService({ + esContext, + savedObjectsService, + }); + + eventLogStartService.getClient(request); + + expect(savedObjectsService.getScopedClient).toHaveBeenCalledWith(request); + + const [{ value: savedObjectsClient }] = savedObjectsService.getScopedClient.mock.results; + + expect(jest.requireMock('./event_log_client').EventLogClient).toHaveBeenCalledWith({ + esContext, + savedObjectsClient, + }); + }); + }); +}); + +function fakeRequest(): KibanaRequest { + const savedObjectsClient = savedObjectsClientMock.create(); + return { + headers: {}, + getBasePath: () => '', + path: '/', + route: { settings: {} }, + url: { + href: '/', + }, + raw: { + req: { + url: '/', + }, + }, + getSavedObjectsClient: () => savedObjectsClient, + } as any; +} diff --git a/x-pack/plugins/event_log/server/event_log_start_service.ts b/x-pack/plugins/event_log/server/event_log_start_service.ts new file mode 100644 index 00000000000000..5938f7a2e614ec --- /dev/null +++ b/x-pack/plugins/event_log/server/event_log_start_service.ts @@ -0,0 +1,48 @@ +/* + * 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 _ from 'lodash'; +import { Observable } from 'rxjs'; +import { + ClusterClient, + KibanaRequest, + SavedObjectsServiceStart, + SavedObjectsClientContract, +} from 'src/core/server'; + +import { EsContext } from './es'; +import { IEventLogClientService } from './types'; +import { EventLogClient } from './event_log_client'; +export type PluginClusterClient = Pick; +export type AdminClusterClient$ = Observable; + +interface EventLogServiceCtorParams { + esContext: EsContext; + savedObjectsService: SavedObjectsServiceStart; +} + +// note that clusterClient may be null, indicating we can't write to ES +export class EventLogClientService implements IEventLogClientService { + private esContext: EsContext; + private savedObjectsService: SavedObjectsServiceStart; + + constructor({ esContext, savedObjectsService }: EventLogServiceCtorParams) { + this.esContext = esContext; + this.savedObjectsService = savedObjectsService; + } + + getClient( + request: KibanaRequest, + savedObjectsClient: SavedObjectsClientContract = this.savedObjectsService.getScopedClient( + request + ) + ) { + return new EventLogClient({ + esContext: this.esContext, + savedObjectsClient, + }); + } +} diff --git a/x-pack/plugins/event_log/server/index.ts b/x-pack/plugins/event_log/server/index.ts index 81a56faa49964c..b7fa25cb6eb9cc 100644 --- a/x-pack/plugins/event_log/server/index.ts +++ b/x-pack/plugins/event_log/server/index.ts @@ -8,6 +8,6 @@ import { PluginInitializerContext } from 'src/core/server'; import { ConfigSchema } from './types'; import { Plugin } from './plugin'; -export { IEventLogService, IEventLogger, IEvent } from './types'; +export { IEventLogService, IEventLogger, IEventLogClientService, IEvent } from './types'; export const config = { schema: ConfigSchema }; export const plugin = (context: PluginInitializerContext) => new Plugin(context); diff --git a/x-pack/plugins/event_log/server/mocks.ts b/x-pack/plugins/event_log/server/mocks.ts index aad6cf3e245616..2f632a52d2f360 100644 --- a/x-pack/plugins/event_log/server/mocks.ts +++ b/x-pack/plugins/event_log/server/mocks.ts @@ -5,8 +5,9 @@ */ import { eventLogServiceMock } from './event_log_service.mock'; +import { eventLogStartServiceMock } from './event_log_start_service.mock'; -export { eventLogServiceMock }; +export { eventLogServiceMock, eventLogStartServiceMock }; export { eventLoggerMock } from './event_logger.mock'; const createSetupMock = () => { @@ -14,7 +15,7 @@ const createSetupMock = () => { }; const createStartMock = () => { - return undefined; + return eventLogStartServiceMock.create(); }; export const eventLogMock = { diff --git a/x-pack/plugins/event_log/server/plugin.ts b/x-pack/plugins/event_log/server/plugin.ts index fdb08b2d090a62..2cc41354b4fbc5 100644 --- a/x-pack/plugins/event_log/server/plugin.ts +++ b/x-pack/plugins/event_log/server/plugin.ts @@ -14,11 +14,21 @@ import { PluginInitializerContext, ClusterClient, SharedGlobalConfig, + IContextProvider, + RequestHandler, } from 'src/core/server'; -import { IEventLogConfig, IEventLogService, IEventLogger, IEventLogConfig$ } from './types'; +import { + IEventLogConfig, + IEventLogService, + IEventLogger, + IEventLogConfig$, + IEventLogClientService, +} from './types'; +import { findRoute } from './routes'; import { EventLogService } from './event_log_service'; import { createEsContext, EsContext } from './es'; +import { EventLogClientService } from './event_log_start_service'; export type PluginClusterClient = Pick; @@ -29,13 +39,14 @@ const ACTIONS = { stopping: 'stopping', }; -export class Plugin implements CorePlugin { +export class Plugin implements CorePlugin { private readonly config$: IEventLogConfig$; private systemLogger: Logger; private eventLogService?: IEventLogService; private esContext?: EsContext; private eventLogger?: IEventLogger; private globalConfig$: Observable; + private eventLogClientService?: EventLogClientService; constructor(private readonly context: PluginInitializerContext) { this.systemLogger = this.context.logger.get(); @@ -71,10 +82,17 @@ export class Plugin implements CorePlugin { event: { provider: PROVIDER }, }); + core.http.registerRouteHandlerContext('eventLog', this.createRouteHandlerContext()); + + // Routes + const router = core.http.createRouter(); + // Register routes + findRoute(router); + return this.eventLogService; } - async start(core: CoreStart) { + async start(core: CoreStart): Promise { this.systemLogger.debug('starting plugin'); if (!this.esContext) throw new Error('esContext not initialized'); @@ -91,8 +109,26 @@ export class Plugin implements CorePlugin { event: { action: ACTIONS.starting }, message: 'eventLog starting', }); + + this.eventLogClientService = new EventLogClientService({ + esContext: this.esContext, + savedObjectsService: core.savedObjects, + }); + return this.eventLogClientService; } + private createRouteHandlerContext = (): IContextProvider< + RequestHandler, + 'eventLog' + > => { + return async (context, request) => { + return { + getEventLogClient: () => + this.eventLogClientService!.getClient(request, context.core.savedObjects.client), + }; + }; + }; + stop() { this.systemLogger.debug('stopping plugin'); diff --git a/x-pack/plugins/event_log/server/routes/_mock_handler_arguments.ts b/x-pack/plugins/event_log/server/routes/_mock_handler_arguments.ts new file mode 100644 index 00000000000000..6640683bf6005e --- /dev/null +++ b/x-pack/plugins/event_log/server/routes/_mock_handler_arguments.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 { RequestHandlerContext, KibanaRequest, KibanaResponseFactory } from 'kibana/server'; +import { identity, merge } from 'lodash'; +import { httpServerMock } from '../../../../../src/core/server/mocks'; +import { IEventLogClient } from '../types'; + +export function mockHandlerArguments( + eventLogClient: IEventLogClient, + req: any, + res?: Array> +): [RequestHandlerContext, KibanaRequest, KibanaResponseFactory] { + return [ + ({ + eventLog: { + getEventLogClient() { + return eventLogClient; + }, + }, + } as unknown) as RequestHandlerContext, + req as KibanaRequest, + mockResponseFactory(res), + ]; +} + +export const mockResponseFactory = (resToMock: Array> = []) => { + const factory: jest.Mocked = httpServerMock.createResponseFactory(); + resToMock.forEach((key: string) => { + if (key in factory) { + Object.defineProperty(factory, key, { + value: jest.fn(identity), + }); + } + }); + return (factory as unknown) as KibanaResponseFactory; +}; + +export function fakeEvent(overrides = {}) { + return merge( + { + event: { + provider: 'actions', + action: 'execute', + start: '2020-03-30T14:55:47.054Z', + end: '2020-03-30T14:55:47.055Z', + duration: 1000000, + }, + kibana: { + namespace: 'default', + saved_objects: [ + { + type: 'action', + id: '968f1b82-0414-4a10-becc-56b6473e4a29', + }, + ], + server_uuid: '5b2de169-2785-441b-ae8c-186a1936b17d', + }, + message: 'action executed: .server-log:968f1b82-0414-4a10-becc-56b6473e4a29: logger', + '@timestamp': '2020-03-30T14:55:47.055Z', + ecs: { + version: '1.3.1', + }, + }, + overrides + ); +} diff --git a/x-pack/plugins/event_log/server/routes/find.test.ts b/x-pack/plugins/event_log/server/routes/find.test.ts new file mode 100644 index 00000000000000..844a84dc117a9c --- /dev/null +++ b/x-pack/plugins/event_log/server/routes/find.test.ts @@ -0,0 +1,98 @@ +/* + * 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 { findRoute } from './find'; +import { mockRouter, RouterMock } from '../../../../../src/core/server/http/router/router.mock'; +import { mockHandlerArguments, fakeEvent } from './_mock_handler_arguments'; +import { eventLogClientMock } from '../event_log_client.mock'; + +const eventLogClient = eventLogClientMock.create(); + +beforeEach(() => { + jest.resetAllMocks(); +}); + +describe('find', () => { + it('finds events with proper parameters', async () => { + const router: RouterMock = mockRouter.create(); + + findRoute(router); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/event_log/{type}/{id}/_find"`); + + const events = [fakeEvent(), fakeEvent()]; + const result = { + page: 0, + per_page: 10, + total: events.length, + data: events, + }; + eventLogClient.findEventsBySavedObject.mockResolvedValueOnce(result); + + const [context, req, res] = mockHandlerArguments( + eventLogClient, + { + params: { id: '1', type: 'action' }, + }, + ['ok'] + ); + + await handler(context, req, res); + + expect(eventLogClient.findEventsBySavedObject).toHaveBeenCalledTimes(1); + + const [type, id] = eventLogClient.findEventsBySavedObject.mock.calls[0]; + expect(type).toEqual(`action`); + expect(id).toEqual(`1`); + + expect(res.ok).toHaveBeenCalledWith({ + body: result, + }); + }); + + it('supports optional pagination parameters', async () => { + const router: RouterMock = mockRouter.create(); + + findRoute(router); + + const [, handler] = router.get.mock.calls[0]; + eventLogClient.findEventsBySavedObject.mockResolvedValueOnce({ + page: 0, + per_page: 10, + total: 0, + data: [], + }); + + const [context, req, res] = mockHandlerArguments( + eventLogClient, + { + params: { id: '1', type: 'action' }, + query: { page: 3, per_page: 10 }, + }, + ['ok'] + ); + + await handler(context, req, res); + + expect(eventLogClient.findEventsBySavedObject).toHaveBeenCalledTimes(1); + + const [type, id, options] = eventLogClient.findEventsBySavedObject.mock.calls[0]; + expect(type).toEqual(`action`); + expect(id).toEqual(`1`); + expect(options).toMatchObject({}); + + expect(res.ok).toHaveBeenCalledWith({ + body: { + page: 0, + per_page: 10, + total: 0, + data: [], + }, + }); + }); +}); diff --git a/x-pack/plugins/event_log/server/routes/find.ts b/x-pack/plugins/event_log/server/routes/find.ts new file mode 100644 index 00000000000000..cb170e50fb4477 --- /dev/null +++ b/x-pack/plugins/event_log/server/routes/find.ts @@ -0,0 +1,50 @@ +/* + * 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 { BASE_EVENT_LOG_API_PATH } from '../../common'; +import { findOptionsSchema, FindOptionsType } from '../event_log_client'; + +const paramSchema = schema.object({ + type: schema.string(), + id: schema.string(), +}); + +export const findRoute = (router: IRouter) => { + router.get( + { + path: `${BASE_EVENT_LOG_API_PATH}/{type}/{id}/_find`, + validate: { + params: paramSchema, + query: findOptionsSchema, + }, + }, + router.handleLegacyErrors(async function( + context: RequestHandlerContext, + req: KibanaRequest, FindOptionsType, any, any>, + res: KibanaResponseFactory + ): Promise> { + if (!context.eventLog) { + return res.badRequest({ body: 'RouteHandlerContext is not registered for eventLog' }); + } + const eventLogClient = context.eventLog.getEventLogClient(); + const { + params: { id, type }, + query, + } = req; + return res.ok({ + body: await eventLogClient.findEventsBySavedObject(type, id, query), + }); + }) + ); +}; diff --git a/x-pack/typings/elastic__node_crypto.d.ts b/x-pack/plugins/event_log/server/routes/index.ts similarity index 86% rename from x-pack/typings/elastic__node_crypto.d.ts rename to x-pack/plugins/event_log/server/routes/index.ts index 463b662d5b207c..85d9b3e0db8cd6 100644 --- a/x-pack/typings/elastic__node_crypto.d.ts +++ b/x-pack/plugins/event_log/server/routes/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -declare module '@elastic/node-crypto'; +export { findRoute } from './find'; diff --git a/x-pack/plugins/event_log/server/types.ts b/x-pack/plugins/event_log/server/types.ts index f606bb2be6c6c1..baf53ef4479141 100644 --- a/x-pack/plugins/event_log/server/types.ts +++ b/x-pack/plugins/event_log/server/types.ts @@ -8,7 +8,10 @@ import { Observable } from 'rxjs'; import { schema, TypeOf } from '@kbn/config-schema'; export { IEvent, IValidatedEvent, EventSchema, ECS_VERSION } from '../generated/schemas'; +import { KibanaRequest } from 'kibana/server'; import { IEvent } from '../generated/schemas'; +import { FindOptionsType } from './event_log_client'; +import { QueryEventsBySavedObjectResult } from './es/cluster_client_adapter'; export const ConfigSchema = schema.object({ enabled: schema.boolean({ defaultValue: true }), @@ -19,6 +22,14 @@ export const ConfigSchema = schema.object({ export type IEventLogConfig = TypeOf; export type IEventLogConfig$ = Observable>; +declare module 'src/core/server' { + interface RequestHandlerContext { + eventLog?: { + getEventLogClient: () => IEventLogClient; + }; + } +} + // the object exposed by plugin.setup() export interface IEventLogService { isEnabled(): boolean; @@ -31,6 +42,18 @@ export interface IEventLogService { getLogger(properties: IEvent): IEventLogger; } +export interface IEventLogClientService { + getClient(request: KibanaRequest): IEventLogClient; +} + +export interface IEventLogClient { + findEventsBySavedObject( + type: string, + id: string, + options?: Partial + ): Promise; +} + export interface IEventLogger { logEvent(properties: IEvent): void; startTiming(event: IEvent): void; diff --git a/x-pack/plugins/features/common/feature.ts b/x-pack/plugins/features/common/feature.ts index 82fcc33f5c8ceb..ef32a8a80a0bd6 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 00000000000000..0186011382e84c --- /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 5b4f7728c9f311..2039f8f6acda2a 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 73a353cd274711..6140b7ac87ce02 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 fdeceb30b4e3d9..403d9586bf1600 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 da4b8e6f6eef24..95630a69818436 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 cd3ba43c3607c7..2e43ede2480ce5 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 0f5b07f8c0e134..d84e46d08a2879 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 09f17023495428..38cd0cec145f90 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 60bba61b75ef14..c5ea65f7a4d1ad 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 2ab3a3322661d1..8808219cabaa70 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 e87ffcfb2b912d..66e0a363c89833 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 557a071ec91752..abed691f109c00 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 5b9207a88e6e47..c7ff6c96382043 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/constants/agent.ts b/x-pack/plugins/ingest_manager/common/constants/agent.ts index fe6f7f57e28996..0b462fb4c03195 100644 --- a/x-pack/plugins/ingest_manager/common/constants/agent.ts +++ b/x-pack/plugins/ingest_manager/common/constants/agent.ts @@ -5,8 +5,8 @@ */ export const AGENT_SAVED_OBJECT_TYPE = 'agents'; - export const AGENT_EVENT_SAVED_OBJECT_TYPE = 'agent_events'; +export const AGENT_ACTION_SAVED_OBJECT_TYPE = 'agent_actions'; export const AGENT_TYPE_PERMANENT = 'PERMANENT'; export const AGENT_TYPE_EPHEMERAL = 'EPHEMERAL'; 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 ab039be8e7c22d..b897c03e89f821 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 f58eaacb7be673..20bbbec8919d63 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 357f4078118809..5fa7af2dda79ab 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/agent.ts b/x-pack/plugins/ingest_manager/common/types/models/agent.ts index aa5729a101e111..4d03a30f9a5904 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/agent.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/agent.ts @@ -16,15 +16,21 @@ export type AgentStatus = 'offline' | 'error' | 'online' | 'inactive' | 'warning export interface NewAgentAction { type: 'CONFIG_CHANGE' | 'DATA_DUMP' | 'RESUME' | 'PAUSE'; - data?: string; + data?: any; sent_at?: string; } export type AgentAction = NewAgentAction & { id: string; + agent_id: string; created_at: string; } & SavedObjectAttributes; +export interface AgentActionSOAttributes extends NewAgentAction, SavedObjectAttributes { + created_at: string; + agent_id: string; +} + export interface AgentEvent { type: 'STATE' | 'ERROR' | 'ACTION_RESULT' | 'ACTION'; subtype: // State @@ -62,7 +68,6 @@ interface AgentBase { config_revision?: number; config_newest_revision?: number; last_checkin?: string; - actions: AgentAction[]; } export interface Agent extends AgentBase { 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 28786530db0189..efa6621001038b 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 356739af1ff9a8..0e8763cb2d4c09 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 74b08f48df12de..6b0c68ccb7d3fa 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 3bf5b2bb4c0f02..43e8f5a2c060db 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 bcb99eed88ac04..846a807f9240d3 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 e5f18e1449d1b2..3bfca756689115 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 23d0f3317a6673..7815ab9cd1d6ed 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 44e5bfa41cb9bf..d99f0712db3c36 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 00000000000000..a45fabeb5ed6a3 --- /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 00000000000000..518e2bfc1af07a --- /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 b45beef4a8b5e4..105d6c66a57047 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 3a6dfe4a87dafe..685199245df18f 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 0aa08602e4d4de..5ebd1300baf65d 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 333a9b049fa853..32615278b67d76 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/constants/index.ts b/x-pack/plugins/ingest_manager/server/constants/index.ts index f6ee475614c5e3..6ac92ca5d2a912 100644 --- a/x-pack/plugins/ingest_manager/server/constants/index.ts +++ b/x-pack/plugins/ingest_manager/server/constants/index.ts @@ -20,8 +20,9 @@ export { INSTALL_SCRIPT_API_ROUTES, SETUP_API_ROUTE, // Saved object types - AGENT_EVENT_SAVED_OBJECT_TYPE, AGENT_SAVED_OBJECT_TYPE, + AGENT_EVENT_SAVED_OBJECT_TYPE, + AGENT_ACTION_SAVED_OBJECT_TYPE, AGENT_CONFIG_SAVED_OBJECT_TYPE, DATASOURCE_SAVED_OBJECT_TYPE, OUTPUT_SAVED_OBJECT_TYPE, diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts index a20ba4a880537d..76247c338a24f5 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.test.ts @@ -78,7 +78,7 @@ describe('test actions handlers', () => { getAgent: jest.fn().mockReturnValueOnce({ id: 'agent', }), - updateAgentActions: jest.fn().mockReturnValueOnce(agentAction), + createAgentAction: jest.fn().mockReturnValueOnce(agentAction), } as jest.Mocked; const postNewAgentActionHandler = postNewAgentActionHandlerBuilder(actionsService); diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts index 2b9c2308035932..8eb427e5739b08 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/actions_handlers.ts @@ -28,11 +28,11 @@ export const postNewAgentActionHandlerBuilder = function( const newAgentAction = request.body.action as NewAgentAction; - const savedAgentAction = await actionsService.updateAgentActions( - soClient, - agent, - newAgentAction - ); + const savedAgentAction = await actionsService.createAgentAction(soClient, { + created_at: new Date().toISOString(), + ...newAgentAction, + agent_id: agent.id, + }); const body: PostNewAgentActionResponse = { success: true, diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts b/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts index adff1fda11200d..89c827abe30ec0 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/handlers.ts @@ -187,8 +187,9 @@ export const postAgentCheckinHandler: RequestHandler< action: 'checkin', success: true, actions: actions.map(a => ({ + agent_id: agent.id, type: a.type, - data: a.data ? JSON.parse(a.data) : a.data, + data: a.data, id: a.id, created_at: a.created_at, })), diff --git a/x-pack/plugins/ingest_manager/server/routes/agent/index.ts b/x-pack/plugins/ingest_manager/server/routes/agent/index.ts index d4610270178429..ac27e47db155e8 100644 --- a/x-pack/plugins/ingest_manager/server/routes/agent/index.ts +++ b/x-pack/plugins/ingest_manager/server/routes/agent/index.ts @@ -122,7 +122,7 @@ export const registerRoutes = (router: IRouter) => { }, postNewAgentActionHandlerBuilder({ getAgent: AgentService.getAgent, - updateAgentActions: AgentService.updateAgentActions, + createAgentAction: AgentService.createAgentAction, }) ); diff --git a/x-pack/plugins/ingest_manager/server/saved_objects.ts b/x-pack/plugins/ingest_manager/server/saved_objects.ts index 9f3035e1aac17b..6800cb4056700a 100644 --- a/x-pack/plugins/ingest_manager/server/saved_objects.ts +++ b/x-pack/plugins/ingest_manager/server/saved_objects.ts @@ -10,6 +10,7 @@ import { PACKAGES_SAVED_OBJECT_TYPE, AGENT_SAVED_OBJECT_TYPE, AGENT_EVENT_SAVED_OBJECT_TYPE, + AGENT_ACTION_SAVED_OBJECT_TYPE, ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE, } from './constants'; @@ -38,17 +39,16 @@ export const savedObjectMappings = { default_api_key: { type: 'keyword' }, updated_at: { type: 'date' }, current_error_events: { type: 'text' }, + }, + }, + [AGENT_ACTION_SAVED_OBJECT_TYPE]: { + properties: { + agent_id: { type: 'keyword' }, + type: { type: 'keyword' }, // FIXME_INGEST https://github.com/elastic/kibana/issues/56554 - actions: { - type: 'nested', - properties: { - id: { type: 'keyword' }, - type: { type: 'keyword' }, - data: { type: 'text' }, - sent_at: { type: 'date' }, - created_at: { type: 'date' }, - }, - }, + data: { type: 'flattened' }, + sent_at: { type: 'date' }, + created_at: { type: 'date' }, }, }, [AGENT_EVENT_SAVED_OBJECT_TYPE]: { @@ -148,6 +148,7 @@ export const savedObjectMappings = { properties: { name: { type: 'keyword' }, version: { type: 'keyword' }, + internal: { type: 'boolean' }, installed: { type: 'nested', properties: { 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 a941494072ae3d..309ddca3784c29 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/ingest_manager/server/services/agents/acks.test.ts b/x-pack/plugins/ingest_manager/server/services/agents/acks.test.ts index 3c07463e3af5de..b4c1f09015a69b 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/acks.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/acks.test.ts @@ -3,29 +3,46 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import Boom from 'boom'; +import { SavedObjectsBulkResponse } from 'kibana/server'; import { savedObjectsClientMock } from '../../../../../../src/core/server/saved_objects/service/saved_objects_client.mock'; -import { Agent, AgentAction, AgentEvent } from '../../../common/types/models'; +import { + Agent, + AgentAction, + AgentActionSOAttributes, + AgentEvent, +} from '../../../common/types/models'; import { AGENT_TYPE_PERMANENT } from '../../../common/constants'; import { acknowledgeAgentActions } from './acks'; -import { isBoom } from 'boom'; describe('test agent acks services', () => { it('should succeed on valid and matched actions', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); + + mockSavedObjectsClient.bulkGet.mockReturnValue( + Promise.resolve({ + saved_objects: [ + { + id: 'action1', + references: [], + type: 'agent_actions', + attributes: { + type: 'CONFIG_CHANGE', + agent_id: 'id', + sent_at: '2020-03-14T19:45:02.620Z', + timestamp: '2019-01-04T14:32:03.36764-05:00', + created_at: '2020-03-14T19:45:02.620Z', + }, + }, + ], + } as SavedObjectsBulkResponse) + ); + const agentActions = await acknowledgeAgentActions( mockSavedObjectsClient, ({ id: 'id', type: AGENT_TYPE_PERMANENT, - actions: [ - { - type: 'CONFIG_CHANGE', - id: 'action1', - sent_at: '2020-03-14T19:45:02.620Z', - timestamp: '2019-01-04T14:32:03.36764-05:00', - created_at: '2020-03-14T19:45:02.620Z', - }, - ], } as unknown) as Agent, [ { @@ -41,6 +58,7 @@ describe('test agent acks services', () => { ({ type: 'CONFIG_CHANGE', id: 'action1', + agent_id: 'id', sent_at: '2020-03-14T19:45:02.620Z', timestamp: '2019-01-04T14:32:03.36764-05:00', created_at: '2020-03-14T19:45:02.620Z', @@ -50,21 +68,26 @@ describe('test agent acks services', () => { it('should fail for actions that cannot be found on agent actions list', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); + mockSavedObjectsClient.bulkGet.mockReturnValue( + Promise.resolve({ + saved_objects: [ + { + id: 'action1', + error: { + message: 'Not found', + statusCode: 404, + }, + }, + ], + } as SavedObjectsBulkResponse) + ); + try { await acknowledgeAgentActions( mockSavedObjectsClient, ({ id: 'id', type: AGENT_TYPE_PERMANENT, - actions: [ - { - type: 'CONFIG_CHANGE', - id: 'action1', - sent_at: '2020-03-14T19:45:02.620Z', - timestamp: '2019-01-04T14:32:03.36764-05:00', - created_at: '2020-03-14T19:45:02.620Z', - }, - ], } as unknown) as Agent, [ ({ @@ -78,27 +101,38 @@ describe('test agent acks services', () => { ); expect(true).toBeFalsy(); } catch (e) { - expect(isBoom(e)).toBeTruthy(); + expect(Boom.isBoom(e)).toBeTruthy(); } }); it('should fail for events that have types not in the allowed acknowledgement type list', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); + + mockSavedObjectsClient.bulkGet.mockReturnValue( + Promise.resolve({ + saved_objects: [ + { + id: 'action1', + references: [], + type: 'agent_actions', + attributes: { + type: 'CONFIG_CHANGE', + agent_id: 'id', + sent_at: '2020-03-14T19:45:02.620Z', + timestamp: '2019-01-04T14:32:03.36764-05:00', + created_at: '2020-03-14T19:45:02.620Z', + }, + }, + ], + } as SavedObjectsBulkResponse) + ); + try { await acknowledgeAgentActions( mockSavedObjectsClient, ({ id: 'id', type: AGENT_TYPE_PERMANENT, - actions: [ - { - type: 'CONFIG_CHANGE', - id: 'action1', - sent_at: '2020-03-14T19:45:02.620Z', - timestamp: '2019-01-04T14:32:03.36764-05:00', - created_at: '2020-03-14T19:45:02.620Z', - }, - ], } as unknown) as Agent, [ ({ @@ -112,7 +146,7 @@ describe('test agent acks services', () => { ); expect(true).toBeFalsy(); } catch (e) { - expect(isBoom(e)).toBeTruthy(); + expect(Boom.isBoom(e)).toBeTruthy(); } }); }); diff --git a/x-pack/plugins/ingest_manager/server/services/agents/acks.ts b/x-pack/plugins/ingest_manager/server/services/agents/acks.ts index cf9a47979ae8bf..24c3b322aad7f3 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/acks.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/acks.ts @@ -17,8 +17,14 @@ import { AgentEvent, AgentEventSOAttributes, AgentSOAttributes, + AgentActionSOAttributes, } from '../../types'; -import { AGENT_EVENT_SAVED_OBJECT_TYPE, AGENT_SAVED_OBJECT_TYPE } from '../../constants'; +import { + AGENT_EVENT_SAVED_OBJECT_TYPE, + AGENT_SAVED_OBJECT_TYPE, + AGENT_ACTION_SAVED_OBJECT_TYPE, +} from '../../constants'; +import { getAgentActionByIds } from './actions'; const ALLOWED_ACKNOWLEDGEMENT_TYPE: string[] = ['ACTION_RESULT']; @@ -27,50 +33,81 @@ export async function acknowledgeAgentActions( agent: Agent, agentEvents: AgentEvent[] ): Promise { - const now = new Date().toISOString(); - - const agentActionMap: Map = new Map( - agent.actions.map(agentAction => [agentAction.id, agentAction]) - ); - - const matchedUpdatedActions: AgentAction[] = []; - - agentEvents.forEach(agentEvent => { + for (const agentEvent of agentEvents) { if (!isAllowedType(agentEvent.type)) { throw Boom.badRequest(`${agentEvent.type} not allowed for acknowledgment only ACTION_RESULT`); } - if (agentActionMap.has(agentEvent.action_id!)) { - const action = agentActionMap.get(agentEvent.action_id!) as AgentAction; - if (!action.sent_at) { - action.sent_at = now; - } - matchedUpdatedActions.push(action); - } else { - throw Boom.badRequest('all actions should belong to current agent'); + } + + const actionIds = agentEvents + .map(event => event.action_id) + .filter(actionId => actionId !== undefined) as string[]; + + let actions; + try { + actions = await getAgentActionByIds(soClient, actionIds); + } catch (error) { + if (Boom.isBoom(error) && error.output.statusCode === 404) { + throw Boom.badRequest(`One or more actions cannot be found`); + } + throw error; + } + + for (const action of actions) { + if (action.agent_id !== agent.id) { + throw Boom.badRequest(`${action.id} not found`); } - }); + } + + if (actions.length === 0) { + return []; + } + const configRevision = getLatestConfigRevison(agent, actions); - if (matchedUpdatedActions.length > 0) { - const configRevision = matchedUpdatedActions.reduce((acc, action) => { - if (action.type !== 'CONFIG_CHANGE') { - return acc; - } - const data = action.data ? JSON.parse(action.data as string) : {}; + await soClient.bulkUpdate([ + buildUpdateAgentConfigRevision(agent.id, configRevision), + ...buildUpdateAgentActionSentAt(actionIds), + ]); - if (data?.config?.id !== agent.config_id) { - return acc; - } + return actions; +} - return data?.config?.revision > acc ? data?.config?.revision : acc; - }, agent.config_revision || 0); +function getLatestConfigRevison(agent: Agent, actions: AgentAction[]) { + return actions.reduce((acc, action) => { + if (action.type !== 'CONFIG_CHANGE') { + return acc; + } + const data = action.data || {}; - await soClient.update(AGENT_SAVED_OBJECT_TYPE, agent.id, { - actions: matchedUpdatedActions, + if (data?.config?.id !== agent.config_id) { + return acc; + } + + return data?.config?.revision > acc ? data?.config?.revision : acc; + }, agent.config_revision || 0); +} + +function buildUpdateAgentConfigRevision(agentId: string, configRevision: number) { + return { + type: AGENT_SAVED_OBJECT_TYPE, + id: agentId, + attributes: { config_revision: configRevision, - }); - } + }, + }; +} - return matchedUpdatedActions; +function buildUpdateAgentActionSentAt( + actionsIds: string[], + sentAt: string = new Date().toISOString() +) { + return actionsIds.map(actionId => ({ + type: AGENT_ACTION_SAVED_OBJECT_TYPE, + id: actionId, + attributes: { + sent_at: sentAt, + }, + })); } function isAllowedType(eventType: string): boolean { diff --git a/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts b/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts index b500aeb825fec7..f2e671c6dbaa87 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/actions.test.ts @@ -4,64 +4,34 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createAgentAction, updateAgentActions } from './actions'; -import { Agent, AgentAction, NewAgentAction } from '../../../common/types/models'; +import { createAgentAction } from './actions'; +import { SavedObject } from 'kibana/server'; +import { AgentAction, AgentActionSOAttributes } from '../../../common/types/models'; import { savedObjectsClientMock } from '../../../../../../src/core/server/saved_objects/service/saved_objects_client.mock'; -import { AGENT_TYPE_PERMANENT } from '../../../common/constants'; - -interface UpdatedActions { - actions: AgentAction[]; -} describe('test agent actions services', () => { - it('should update agent current actions with new action', async () => { + it('should create a new action', async () => { const mockSavedObjectsClient = savedObjectsClientMock.create(); - const newAgentAction: NewAgentAction = { + const newAgentAction: AgentActionSOAttributes = { + agent_id: 'agentid', type: 'CONFIG_CHANGE', data: 'data', sent_at: '2020-03-14T19:45:02.620Z', + created_at: '2020-03-14T19:45:02.620Z', }; - - await updateAgentActions( - mockSavedObjectsClient, - ({ - id: 'id', - type: AGENT_TYPE_PERMANENT, - actions: [ - { - type: 'CONFIG_CHANGE', - id: 'action1', - sent_at: '2020-03-14T19:45:02.620Z', - timestamp: '2019-01-04T14:32:03.36764-05:00', - created_at: '2020-03-14T19:45:02.620Z', - }, - ], - } as unknown) as Agent, - newAgentAction + mockSavedObjectsClient.create.mockReturnValue( + Promise.resolve({ + attributes: {}, + } as SavedObject) ); - - const updatedAgentActions = (mockSavedObjectsClient.update.mock - .calls[0][2] as unknown) as UpdatedActions; - - expect(updatedAgentActions.actions.length).toEqual(2); - const actualAgentAction = updatedAgentActions.actions.find(action => action?.data === 'data'); - expect(actualAgentAction?.type).toEqual(newAgentAction.type); - expect(actualAgentAction?.data).toEqual(newAgentAction.data); - expect(actualAgentAction?.sent_at).toEqual(newAgentAction.sent_at); - }); - - it('should create agent action from new agent action model', async () => { - const newAgentAction: NewAgentAction = { - type: 'CONFIG_CHANGE', - data: 'data', - sent_at: '2020-03-14T19:45:02.620Z', - }; - const now = new Date(); - const agentAction = createAgentAction(now, newAgentAction); - - expect(agentAction.type).toEqual(newAgentAction.type); - expect(agentAction.data).toEqual(newAgentAction.data); - expect(agentAction.sent_at).toEqual(newAgentAction.sent_at); + await createAgentAction(mockSavedObjectsClient, newAgentAction); + + const createdAction = (mockSavedObjectsClient.create.mock + .calls[0][1] as unknown) as AgentAction; + expect(createdAction).toBeDefined(); + expect(createdAction?.type).toEqual(newAgentAction.type); + expect(createdAction?.data).toEqual(newAgentAction.data); + expect(createdAction?.sent_at).toEqual(newAgentAction.sent_at); }); }); diff --git a/x-pack/plugins/ingest_manager/server/services/agents/actions.ts b/x-pack/plugins/ingest_manager/server/services/agents/actions.ts index 2f8ed9f504453a..a8ef0820f8d9f3 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/actions.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/actions.ts @@ -5,46 +5,52 @@ */ import { SavedObjectsClientContract } from 'kibana/server'; -import uuid from 'uuid'; -import { - Agent, - AgentAction, - AgentSOAttributes, - NewAgentAction, -} from '../../../common/types/models'; -import { AGENT_SAVED_OBJECT_TYPE } from '../../../common/constants'; - -export async function updateAgentActions( +import { Agent, AgentAction, AgentActionSOAttributes } from '../../../common/types/models'; +import { AGENT_ACTION_SAVED_OBJECT_TYPE } from '../../../common/constants'; +import { savedObjectToAgentAction } from './saved_objects'; + +export async function createAgentAction( soClient: SavedObjectsClientContract, - agent: Agent, - newAgentAction: NewAgentAction + newAgentAction: AgentActionSOAttributes ): Promise { - const agentAction = createAgentAction(new Date(), newAgentAction); + const so = await soClient.create(AGENT_ACTION_SAVED_OBJECT_TYPE, { + ...newAgentAction, + }); - agent.actions.push(agentAction); + return savedObjectToAgentAction(so); +} - await soClient.update(AGENT_SAVED_OBJECT_TYPE, agent.id, { - actions: agent.actions, +export async function getAgentActionsForCheckin( + soClient: SavedObjectsClientContract, + agentId: string +): Promise { + const res = await soClient.find({ + type: AGENT_ACTION_SAVED_OBJECT_TYPE, + filter: `not ${AGENT_ACTION_SAVED_OBJECT_TYPE}.attributes.sent_at: * and ${AGENT_ACTION_SAVED_OBJECT_TYPE}.attributes.agent_id:${agentId}`, }); - return agentAction; + return res.saved_objects.map(savedObjectToAgentAction); } -export function createAgentAction(createdAt: Date, newAgentAction: NewAgentAction): AgentAction { - const agentAction = { - id: uuid.v4(), - created_at: createdAt.toISOString(), - }; - - return Object.assign(agentAction, newAgentAction); +export async function getAgentActionByIds( + soClient: SavedObjectsClientContract, + actionIds: string[] +) { + const res = await soClient.bulkGet( + actionIds.map(actionId => ({ + id: actionId, + type: AGENT_ACTION_SAVED_OBJECT_TYPE, + })) + ); + + return res.saved_objects.map(savedObjectToAgentAction); } export interface ActionsService { getAgent: (soClient: SavedObjectsClientContract, agentId: string) => Promise; - updateAgentActions: ( + createAgentAction: ( soClient: SavedObjectsClientContract, - agent: Agent, - newAgentAction: NewAgentAction + newAgentAction: AgentActionSOAttributes ) => Promise; } diff --git a/x-pack/plugins/ingest_manager/server/services/agents/checkin.test.ts b/x-pack/plugins/ingest_manager/server/services/agents/checkin.test.ts index d3e10fcb6b63f2..d98052ea87e864 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/checkin.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/checkin.test.ts @@ -14,13 +14,13 @@ function getAgent(data: Partial) { describe('Agent checkin service', () => { describe('shouldCreateConfigAction', () => { it('should return false if the agent do not have an assigned config', () => { - const res = shouldCreateConfigAction(getAgent({})); + const res = shouldCreateConfigAction(getAgent({}), []); expect(res).toBeFalsy(); }); it('should return true if this is agent first checkin', () => { - const res = shouldCreateConfigAction(getAgent({ config_id: 'config1' })); + const res = shouldCreateConfigAction(getAgent({ config_id: 'config1' }), []); expect(res).toBeTruthy(); }); @@ -32,7 +32,8 @@ describe('Agent checkin service', () => { last_checkin: '2018-01-02T00:00:00', config_revision: 1, config_newest_revision: 1, - }) + }), + [] ); expect(res).toBeFalsy(); @@ -45,20 +46,21 @@ describe('Agent checkin service', () => { last_checkin: '2018-01-02T00:00:00', config_revision: 1, config_newest_revision: 2, - actions: [ - { - id: 'action1', - type: 'CONFIG_CHANGE', - created_at: new Date().toISOString(), - data: JSON.stringify({ - config: { - id: 'config1', - revision: 2, - }, - }), - }, - ], - }) + }), + [ + { + id: 'action1', + agent_id: 'agent1', + type: 'CONFIG_CHANGE', + created_at: new Date().toISOString(), + data: JSON.stringify({ + config: { + id: 'config1', + revision: 2, + }, + }), + }, + ] ); expect(res).toBeFalsy(); @@ -71,31 +73,33 @@ describe('Agent checkin service', () => { last_checkin: '2018-01-02T00:00:00', config_revision: 1, config_newest_revision: 2, - actions: [ - { - id: 'action1', - type: 'CONFIG_CHANGE', - created_at: new Date().toISOString(), - data: JSON.stringify({ - config: { - id: 'config2', - revision: 2, - }, - }), - }, - { - id: 'action1', - type: 'CONFIG_CHANGE', - created_at: new Date().toISOString(), - data: JSON.stringify({ - config: { - id: 'config1', - revision: 1, - }, - }), - }, - ], - }) + }), + [ + { + id: 'action1', + agent_id: 'agent1', + type: 'CONFIG_CHANGE', + created_at: new Date().toISOString(), + data: JSON.stringify({ + config: { + id: 'config2', + revision: 2, + }, + }), + }, + { + id: 'action1', + agent_id: 'agent1', + type: 'CONFIG_CHANGE', + created_at: new Date().toISOString(), + data: JSON.stringify({ + config: { + id: 'config1', + revision: 1, + }, + }), + }, + ] ); expect(res).toBeTruthy(); @@ -108,7 +112,8 @@ describe('Agent checkin service', () => { last_checkin: '2018-01-02T00:00:00', config_revision: 1, config_newest_revision: 2, - }) + }), + [] ); expect(res).toBeTruthy(); diff --git a/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts b/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts index d80fff5d8eceba..9a2b3f22b94311 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/checkin.ts @@ -5,7 +5,6 @@ */ import { SavedObjectsClientContract, SavedObjectsBulkCreateObject } from 'src/core/server'; -import uuid from 'uuid'; import { Agent, AgentEvent, @@ -17,6 +16,7 @@ import { import { agentConfigService } from '../agent_config'; import * as APIKeysService from '../api_keys'; import { AGENT_SAVED_OBJECT_TYPE, AGENT_EVENT_SAVED_OBJECT_TYPE } from '../../constants'; +import { getAgentActionsForCheckin, createAgentAction } from './actions'; export async function agentCheckin( soClient: SavedObjectsClientContract, @@ -34,10 +34,10 @@ export async function agentCheckin( last_checkin: new Date().toISOString(), }; - const actions = filterActionsForCheckin(agent); + const actions = await getAgentActionsForCheckin(soClient, agent.id); // Generate new agent config if config is updated - if (agent.config_id && shouldCreateConfigAction(agent)) { + if (agent.config_id && shouldCreateConfigAction(agent, actions)) { const config = await agentConfigService.getFullConfig(soClient, agent.config_id); if (config) { // Assign output API keys @@ -52,18 +52,14 @@ export async function agentCheckin( // Mutate the config to set the api token for this agent config.outputs.default.api_key = agent.default_api_key || updateData.default_api_key; - const configChangeAction: AgentAction = { - id: uuid.v4(), + const configChangeAction = await createAgentAction(soClient, { + agent_id: agent.id, type: 'CONFIG_CHANGE', + data: { config } as any, created_at: new Date().toISOString(), - data: JSON.stringify({ - config, - }), sent_at: undefined, - }; + }); actions.push(configChangeAction); - // persist new action - updateData.actions = actions; } } if (localMetadata) { @@ -149,7 +145,7 @@ function isActionEvent(event: AgentEvent) { ); } -export function shouldCreateConfigAction(agent: Agent): boolean { +export function shouldCreateConfigAction(agent: Agent, actions: AgentAction[]): boolean { if (!agent.config_id) { return false; } @@ -167,7 +163,7 @@ export function shouldCreateConfigAction(agent: Agent): boolean { return false; } - const isActionAlreadyGenerated = !!agent.actions.find(action => { + const isActionAlreadyGenerated = !!actions.find(action => { if (!action.data || action.type !== 'CONFIG_CHANGE') { return false; } @@ -181,7 +177,3 @@ export function shouldCreateConfigAction(agent: Agent): boolean { return !isActionAlreadyGenerated; } - -function filterActionsForCheckin(agent: Agent): AgentAction[] { - return agent.actions.filter((a: AgentAction) => !a.sent_at); -} diff --git a/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts b/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts index 52547e9bcb0fb0..a34d2e03e9b3de 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/enroll.ts @@ -35,7 +35,6 @@ export async function enroll( user_provided_metadata: JSON.stringify(metadata?.userProvided ?? {}), local_metadata: JSON.stringify(metadata?.local ?? {}), current_error_events: undefined, - actions: [], access_api_key_id: undefined, last_checkin: undefined, default_api_key: undefined, diff --git a/x-pack/plugins/ingest_manager/server/services/agents/saved_objects.ts b/x-pack/plugins/ingest_manager/server/services/agents/saved_objects.ts index dbe268818713d9..aa885207406872 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/saved_objects.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/saved_objects.ts @@ -4,8 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ +import Boom from 'boom'; import { SavedObject } from 'src/core/server'; -import { Agent, AgentSOAttributes } from '../../types'; +import { Agent, AgentSOAttributes, AgentAction, AgentActionSOAttributes } from '../../types'; export function savedObjectToAgent(so: SavedObject): Agent { if (so.error) { @@ -24,3 +25,18 @@ export function savedObjectToAgent(so: SavedObject): Agent { status: undefined, }; } + +export function savedObjectToAgentAction(so: SavedObject): AgentAction { + if (so.error) { + if (so.error.statusCode === 404) { + throw Boom.notFound(so.error.message); + } + + throw new Error(so.error.message); + } + + return { + id: so.id, + ...so.attributes, + }; +} diff --git a/x-pack/plugins/ingest_manager/server/services/epm/fields/field.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/fields/field.test.ts index 929f2518ee748e..e3aef6077dbc30 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/fields/field.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/fields/field.test.ts @@ -80,3 +80,103 @@ describe('getField searches recursively for nested field in fields given an arra expect(getField(searchFields, ['2', '2-2', '2-2-1'])?.name).toBe('2-2-1'); }); }); + +describe('processFields', () => { + const flattenedFields = [ + { + name: 'a.a', + type: 'text', + }, + { + name: 'a.b', + type: 'text', + }, + ]; + const expandedFields = [ + { + name: 'a', + type: 'group', + fields: [ + { + name: 'a', + type: 'text', + }, + { + name: 'b', + type: 'text', + }, + ], + }, + ]; + test('correctly expands flattened fields', () => { + expect(JSON.stringify(processFields(flattenedFields))).toEqual(JSON.stringify(expandedFields)); + }); + test('leaves expanded fields unchanged', () => { + expect(JSON.stringify(processFields(expandedFields))).toEqual(JSON.stringify(expandedFields)); + }); + + const mixedFieldsA = [ + { + name: 'a.a', + type: 'group', + fields: [ + { + name: 'a', + type: 'text', + }, + { + name: 'b', + type: 'text', + }, + ], + }, + ]; + + const mixedFieldsB = [ + { + name: 'a', + type: 'group', + fields: [ + { + name: 'a.a', + type: 'text', + }, + { + name: 'a.b', + type: 'text', + }, + ], + }, + ]; + + const mixedFieldsExpanded = [ + { + name: 'a', + type: 'group', + fields: [ + { + name: 'a', + type: 'group', + fields: [ + { + name: 'a', + type: 'text', + }, + { + name: 'b', + type: 'text', + }, + ], + }, + ], + }, + ]; + test('correctly expands a mix of expanded and flattened fields', () => { + expect(JSON.stringify(processFields(mixedFieldsA))).toEqual( + JSON.stringify(mixedFieldsExpanded) + ); + expect(JSON.stringify(processFields(mixedFieldsB))).toEqual( + JSON.stringify(mixedFieldsExpanded) + ); + }); +}); diff --git a/x-pack/plugins/ingest_manager/server/services/epm/fields/field.ts b/x-pack/plugins/ingest_manager/server/services/epm/fields/field.ts index 4a1a84baf6599e..810896bb50389f 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/fields/field.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/fields/field.ts @@ -52,13 +52,12 @@ export type Fields = Field[]; * expandFields takes the given fields read from yaml and expands them. * There are dotted fields in the field.yml like `foo.bar`. These should * be stored as an field within a 'group' field. - * - * Note: This function modifies the passed fields array. */ -export function expandFields(fields: Fields) { +export function expandFields(fields: Fields): Fields { + const newFields: Fields = []; + fields.forEach((field, key) => { const fieldName = field.name; - // If the field name contains a dot, it means we need to // - take the first part of the name // - create a field of type 'group' with this first part @@ -71,30 +70,29 @@ export function expandFields(fields: Fields) { const groupFieldName = nameParts[0]; // Put back together the parts again for the new field name - const restFieldName = nameParts.slice(1).join('.'); + const nestedFieldName = nameParts.slice(1).join('.'); // keep all properties of the original field, but give it the shortened name - field.name = restFieldName; + const nestedField = { ...field, name: nestedFieldName }; // create a new field of type group with the original field in the fields array const groupField: Field = { name: groupFieldName, type: 'group', - fields: [field], + fields: expandFields([nestedField]), }; - // check child fields further down the tree - if (groupField.fields) { - expandFields(groupField.fields); - } // Replace the original field in the array with the new one - fields[key] = groupField; + newFields.push(groupField); } else { // even if this field doesn't have dots to expand, its child fields further down the tree might - if (field.fields) { - expandFields(field.fields); + const newField = { ...field }; + if (newField.fields) { + newField.fields = expandFields(newField.fields); } + newFields.push(newField); } }); + return newFields; } /** * dedupFields takes the given fields and merges sibling fields with the @@ -180,8 +178,8 @@ export const getField = (fields: Fields, pathNames: string[]): Field | undefined }; export function processFields(fields: Fields): Fields { - expandFields(fields); - const dedupedFields = dedupFields(fields); + const expandedFields = expandFields(fields); + const dedupedFields = dedupFields(expandedFields); return validateFields(dedupedFields, dedupedFields); } diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/get.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/get.ts index d655b81f8cdef9..e963ea138dfd5e 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/packages/get.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/get.ts @@ -31,17 +31,17 @@ export async function getPackages( Object.assign({}, item, { title: item.title || nameAsTitle(item.name) }) ); }); - const searchObjects = registryItems.map(({ name, version }) => ({ + // get the installed packages + const results = await savedObjectsClient.find({ type: PACKAGES_SAVED_OBJECT_TYPE, - id: `${name}-${version}`, - })); - const results = await savedObjectsClient.bulkGet(searchObjects); - const savedObjects = results.saved_objects.filter(o => !o.error); // ignore errors for now + }); + // filter out any internal packages + const savedObjectsVisible = results.saved_objects.filter(o => !o.attributes.internal); const packageList = registryItems .map(item => createInstallableFrom( item, - savedObjects.find(({ id }) => id === `${item.name}-${item.version}`) + savedObjectsVisible.find(({ attributes }) => attributes.name === item.name) ) ) .sort(sortByName); diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts index 3cce238f582f4f..82523e37509d10 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts @@ -87,7 +87,7 @@ export async function installPackage(options: { }): Promise { const { savedObjectsClient, pkgkey, callCluster } = options; const registryPackageInfo = await Registry.fetchInfo(pkgkey); - const { name: pkgName, version: pkgVersion } = registryPackageInfo; + const { name: pkgName, version: pkgVersion, internal = false } = registryPackageInfo; const installKibanaAssetsPromise = installKibanaAssets({ savedObjectsClient, @@ -116,6 +116,7 @@ export async function installPackage(options: { pkgkey, pkgName, pkgVersion, + internal, toSave, }); return toSave; @@ -145,9 +146,10 @@ export async function saveInstallationReferences(options: { pkgkey: string; pkgName: string; pkgVersion: string; + internal: boolean; toSave: AssetReference[]; }) { - const { savedObjectsClient, pkgkey, pkgName, pkgVersion, toSave } = options; + const { savedObjectsClient, pkgkey, pkgName, pkgVersion, internal, toSave } = options; const installation = await getInstallation({ savedObjectsClient, pkgkey }); const savedRefs = installation?.installed || []; const mergeRefsReducer = (current: AssetReference[], pending: AssetReference) => { @@ -159,7 +161,7 @@ export async function saveInstallationReferences(options: { const toInstall = toSave.reduce(mergeRefsReducer, savedRefs); await savedObjectsClient.create( PACKAGES_SAVED_OBJECT_TYPE, - { installed: toInstall, name: pkgName, version: pkgVersion }, + { installed: toInstall, name: pkgName, version: pkgVersion, internal }, { id: pkgkey, overwrite: true } ); diff --git a/x-pack/plugins/ingest_manager/server/types/index.tsx b/x-pack/plugins/ingest_manager/server/types/index.tsx index 59c7f152e5cbcd..1cd5622c0c7b04 100644 --- a/x-pack/plugins/ingest_manager/server/types/index.tsx +++ b/x-pack/plugins/ingest_manager/server/types/index.tsx @@ -14,6 +14,7 @@ export { AgentEvent, AgentEventSOAttributes, AgentAction, + AgentActionSOAttributes, Datasource, NewDatasource, FullAgentConfigDatasource, diff --git a/x-pack/plugins/ingest_manager/server/types/models/agent.ts b/x-pack/plugins/ingest_manager/server/types/models/agent.ts index f70b3cf0ed092b..f18846348432b0 100644 --- a/x-pack/plugins/ingest_manager/server/types/models/agent.ts +++ b/x-pack/plugins/ingest_manager/server/types/models/agent.ts @@ -60,6 +60,6 @@ export const NewAgentActionSchema = schema.object({ schema.literal('RESUME'), schema.literal('PAUSE'), ]), - data: schema.maybe(schema.string()), + data: schema.maybe(schema.any()), sent_at: schema.maybe(schema.string()), }); diff --git a/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.d.ts b/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.d.ts index ca0e474491780d..ceba2fe56db12c 100644 --- a/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.d.ts +++ b/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.d.ts @@ -5,6 +5,7 @@ */ /* eslint-disable @typescript-eslint/consistent-type-definitions */ +import { RENDER_AS, SORT_ORDER, SCALING_TYPES } from '../constants'; import { MapExtent, MapQuery } from './map_descriptor'; // Global map state passed to every layer. @@ -18,12 +19,26 @@ export type MapFilters = { zoom: number; }; +type ESSearchSourceSyncMeta = { + sortField: string; + sortOrder: SORT_ORDER; + scalingType: SCALING_TYPES; + topHitsSplitField: string; + topHitsSize: number; +}; + +type ESGeoGridSourceSyncMeta = { + requestType: RENDER_AS; +}; + +export type VectorSourceSyncMeta = ESSearchSourceSyncMeta | ESGeoGridSourceSyncMeta; + export type VectorSourceRequestMeta = MapFilters & { applyGlobalQuery: boolean; fieldNames: string[]; geogridPrecision: number; sourceQuery: MapQuery; - sourceMeta: unknown; + sourceMeta: VectorSourceSyncMeta; }; export type VectorStyleRequestMeta = MapFilters & { diff --git a/x-pack/plugins/maps/public/actions/map_actions.d.ts b/x-pack/plugins/maps/public/actions/map_actions.d.ts new file mode 100644 index 00000000000000..debead3ad5c45d --- /dev/null +++ b/x-pack/plugins/maps/public/actions/map_actions.d.ts @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +/* eslint-disable @typescript-eslint/consistent-type-definitions */ + +import { Filter, Query, TimeRange } from 'src/plugins/data/public'; +import { AnyAction } from 'redux'; +import { LAYER_TYPE } from '../../common/constants'; +import { + DataMeta, + MapFilters, + MapCenterAndZoom, + MapRefreshConfig, +} from '../../common/descriptor_types'; + +export type SyncContext = { + startLoading(dataId: string, requestToken: symbol, meta: DataMeta): void; + stopLoading(dataId: string, requestToken: symbol, data: unknown, meta: DataMeta): void; + onLoadError(dataId: string, requestToken: symbol, errorMessage: string): void; + updateSourceData(newData: unknown): void; + isRequestStillActive(dataId: string, requestToken: symbol): boolean; + registerCancelCallback(requestToken: symbol, callback: () => void): void; + dataFilters: MapFilters; +}; + +export function updateSourceProp( + layerId: string, + propName: string, + value: unknown, + newLayerType?: LAYER_TYPE +): void; + +export function setGotoWithCenter(config: MapCenterAndZoom): AnyAction; + +export function replaceLayerList(layerList: unknown[]): AnyAction; + +export type QueryGroup = { + filters: Filter[]; + query?: Query; + timeFilters?: TimeRange; + refresh?: boolean; +}; + +export function setQuery(query: QueryGroup): AnyAction; + +export function setRefreshConfig(config: MapRefreshConfig): AnyAction; + +export function disableScrollZoom(): AnyAction; + +export function disableInteractive(): AnyAction; + +export function disableTooltipControl(): AnyAction; + +export function hideToolbarOverlay(): AnyAction; + +export function hideLayerControl(): AnyAction; + +export function hideViewControl(): AnyAction; + +export function setHiddenLayers(hiddenLayerIds: string[]): AnyAction; + +export function addLayerWithoutDataSync(layerDescriptor: unknown): AnyAction; diff --git a/x-pack/legacy/plugins/maps/public/components/__snapshots__/add_tooltip_field_popover.test.js.snap b/x-pack/plugins/maps/public/components/__snapshots__/add_tooltip_field_popover.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/components/__snapshots__/add_tooltip_field_popover.test.js.snap rename to x-pack/plugins/maps/public/components/__snapshots__/add_tooltip_field_popover.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/components/__snapshots__/tooltip_selector.test.js.snap b/x-pack/plugins/maps/public/components/__snapshots__/tooltip_selector.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/components/__snapshots__/tooltip_selector.test.js.snap rename to x-pack/plugins/maps/public/components/__snapshots__/tooltip_selector.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/components/__snapshots__/validated_range.test.js.snap b/x-pack/plugins/maps/public/components/__snapshots__/validated_range.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/components/__snapshots__/validated_range.test.js.snap rename to x-pack/plugins/maps/public/components/__snapshots__/validated_range.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/components/add_tooltip_field_popover.js b/x-pack/plugins/maps/public/components/add_tooltip_field_popover.js similarity index 98% rename from x-pack/legacy/plugins/maps/public/components/add_tooltip_field_popover.js rename to x-pack/plugins/maps/public/components/add_tooltip_field_popover.js index 07bc54663c1d88..984ace4fd87082 100644 --- a/x-pack/legacy/plugins/maps/public/components/add_tooltip_field_popover.js +++ b/x-pack/plugins/maps/public/components/add_tooltip_field_popover.js @@ -17,7 +17,7 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { FieldIcon } from '../../../../../../src/plugins/kibana_react/public'; +import { FieldIcon } from '../../../../../src/plugins/kibana_react/public'; const sortByLabel = (a, b) => { return a.label.localeCompare(b.label); diff --git a/x-pack/legacy/plugins/maps/public/components/add_tooltip_field_popover.test.js b/x-pack/plugins/maps/public/components/add_tooltip_field_popover.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/components/add_tooltip_field_popover.test.js rename to x-pack/plugins/maps/public/components/add_tooltip_field_popover.test.js diff --git a/x-pack/legacy/plugins/maps/public/components/metric_editor.js b/x-pack/plugins/maps/public/components/metric_editor.js similarity index 94% rename from x-pack/legacy/plugins/maps/public/components/metric_editor.js rename to x-pack/plugins/maps/public/components/metric_editor.js index 530f402592b2ba..d1affe2f42190f 100644 --- a/x-pack/legacy/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/legacy/plugins/maps/public/components/metric_select.js b/x-pack/plugins/maps/public/components/metric_select.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/components/metric_select.js rename to x-pack/plugins/maps/public/components/metric_select.js diff --git a/x-pack/legacy/plugins/maps/public/components/metrics_editor.js b/x-pack/plugins/maps/public/components/metrics_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/components/metrics_editor.js rename to x-pack/plugins/maps/public/components/metrics_editor.js diff --git a/x-pack/legacy/plugins/maps/public/components/no_index_pattern_callout.js b/x-pack/plugins/maps/public/components/no_index_pattern_callout.js similarity index 85% rename from x-pack/legacy/plugins/maps/public/components/no_index_pattern_callout.js rename to x-pack/plugins/maps/public/components/no_index_pattern_callout.js index 3266f13155ca71..13196075468088 100644 --- a/x-pack/legacy/plugins/maps/public/components/no_index_pattern_callout.js +++ b/x-pack/plugins/maps/public/components/no_index_pattern_callout.js @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import chrome from 'ui/chrome'; - +import { getHttp } from '../kibana_services'; import React from 'react'; import { EuiCallOut, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; export function NoIndexPatternCallout() { + const http = getHttp(); return ( - + - + { + const image = new Image(); + if (isCrossOriginUrl(imgUrl)) { + image.crossOrigin = 'Anonymous'; + } + image.onload = el => { + const imgData = getImageData(el.currentTarget); + resolve(imgData); + }; + image.onerror = e => { + reject(e); + }; + image.src = imgUrl; + }); +} + +export function addSpriteSheetToMapFromImageData(json, imgData, mbMap) { + for (const imageId in json) { + if (!(json.hasOwnProperty(imageId) && !mbMap.hasImage(imageId))) { + continue; + } + const { width, height, x, y, sdf, pixelRatio } = json[imageId]; + if (typeof width !== 'number' || typeof height !== 'number') { + continue; + } + + const data = new RGBAImage({ width, height }); + RGBAImage.copy(imgData, data, { x, y }, { x: 0, y: 0 }, { width, height }); + mbMap.addImage(imageId, data, { pixelRatio, sdf }); + } +} diff --git a/x-pack/legacy/plugins/maps/public/elasticsearch_geo_utils.js b/x-pack/plugins/maps/public/elasticsearch_geo_utils.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/elasticsearch_geo_utils.js rename to x-pack/plugins/maps/public/elasticsearch_geo_utils.js diff --git a/x-pack/legacy/plugins/maps/public/elasticsearch_geo_utils.test.js b/x-pack/plugins/maps/public/elasticsearch_geo_utils.test.js similarity index 99% rename from x-pack/legacy/plugins/maps/public/elasticsearch_geo_utils.test.js rename to x-pack/plugins/maps/public/elasticsearch_geo_utils.test.js index fb4b0a6e29e6c4..5db7556be4639d 100644 --- a/x-pack/legacy/plugins/maps/public/elasticsearch_geo_utils.test.js +++ b/x-pack/plugins/maps/public/elasticsearch_geo_utils.test.js @@ -20,7 +20,7 @@ import { convertMapExtentToPolygon, roundCoordinates, } from './elasticsearch_geo_utils'; -import { indexPatterns } from '../../../../../src/plugins/data/public'; +import { indexPatterns } from '../../../../src/plugins/data/public'; const geoFieldName = 'location'; const mapExtent = { diff --git a/x-pack/legacy/plugins/maps/public/index_pattern_util.js b/x-pack/plugins/maps/public/index_pattern_util.js similarity index 95% rename from x-pack/legacy/plugins/maps/public/index_pattern_util.js rename to x-pack/plugins/maps/public/index_pattern_util.js index 30a0a6826db831..6cb02c7605e285 100644 --- a/x-pack/legacy/plugins/maps/public/index_pattern_util.js +++ b/x-pack/plugins/maps/public/index_pattern_util.js @@ -5,7 +5,7 @@ */ import { getIndexPatternService } from './kibana_services'; -import { indexPatterns } from '../../../../../src/plugins/data/public'; +import { indexPatterns } from '../../../../src/plugins/data/public'; import { ES_GEO_FIELD_TYPE } from '../common/constants'; export async function getIndexPatternsFromIds(indexPatternIds = []) { diff --git a/x-pack/legacy/plugins/maps/public/index_pattern_util.test.js b/x-pack/plugins/maps/public/index_pattern_util.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/index_pattern_util.test.js rename to x-pack/plugins/maps/public/index_pattern_util.test.js diff --git a/x-pack/plugins/maps/public/kibana_services.js b/x-pack/plugins/maps/public/kibana_services.js index 1073e44fa711e7..d2ddecfdf915bb 100644 --- a/x-pack/plugins/maps/public/kibana_services.js +++ b/x-pack/plugins/maps/public/kibana_services.js @@ -3,7 +3,89 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ +import { esFilters, search } from '../../../../src/plugins/data/public'; + +export { SearchSource } from '../../../../src/plugins/data/public'; + +export const SPATIAL_FILTER_TYPE = esFilters.FILTERS.SPATIAL_FILTER; +const { getRequestInspectorStats, getResponseInspectorStats } = search; + +let indexPatternService; +export const setIndexPatternService = dataIndexPatterns => + (indexPatternService = dataIndexPatterns); +export const getIndexPatternService = () => indexPatternService; + +let autocompleteService; +export const setAutocompleteService = dataAutoComplete => (autocompleteService = dataAutoComplete); +export const getAutocompleteService = () => autocompleteService; + +let licenseId; +export const setLicenseId = latestLicenseId => (licenseId = latestLicenseId); +export const getLicenseId = () => { + return licenseId; +}; + +let inspector; +export const setInspector = newInspector => (inspector = newInspector); +export const getInspector = () => { + return inspector; +}; + +let fileUploadPlugin; +export const setFileUpload = fileUpload => (fileUploadPlugin = fileUpload); +export const getFileUploadComponent = () => { + return fileUploadPlugin.JsonUploadAndParse; +}; let getInjectedVar; export const setInjectedVarFunc = getInjectedVarFunc => (getInjectedVar = getInjectedVarFunc); export const getInjectedVarFunc = () => getInjectedVar; + +let uiSettings; +export const setUiSettings = coreUiSettings => (uiSettings = coreUiSettings); +export const getUiSettings = () => uiSettings; + +let indexPatternSelectComponent; +export const setIndexPatternSelect = indexPatternSelect => + (indexPatternSelectComponent = indexPatternSelect); +export const getIndexPatternSelectComponent = () => indexPatternSelectComponent; + +let coreHttp; +export const setHttp = http => (coreHttp = http); +export const getHttp = () => coreHttp; + +let dataTimeFilter; +export const setTimeFilter = timeFilter => (dataTimeFilter = timeFilter); +export const getTimeFilter = () => dataTimeFilter; + +let toast; +export const setToasts = notificationToast => (toast = notificationToast); +export const getToasts = () => toast; + +export async function fetchSearchSourceAndRecordWithInspector({ + searchSource, + requestId, + requestName, + requestDesc, + inspectorAdapters, + abortSignal, +}) { + const inspectorRequest = inspectorAdapters.requests.start(requestName, { + id: requestId, + description: requestDesc, + }); + let resp; + try { + inspectorRequest.stats(getRequestInspectorStats(searchSource)); + searchSource.getSearchRequestBody().then(body => { + inspectorRequest.json(body); + }); + resp = await searchSource.fetch({ abortSignal }); + inspectorRequest.stats(getResponseInspectorStats(searchSource, resp)).ok({ json: resp }); + } catch (error) { + inspectorRequest.error({ error }); + throw error; + } + + return resp; +} diff --git a/x-pack/plugins/maps/public/layers/_index.scss b/x-pack/plugins/maps/public/layers/_index.scss new file mode 100644 index 00000000000000..29a57612552784 --- /dev/null +++ b/x-pack/plugins/maps/public/layers/_index.scss @@ -0,0 +1 @@ +@import 'styles/index'; diff --git a/x-pack/legacy/plugins/maps/public/layers/blended_vector_layer.ts b/x-pack/plugins/maps/public/layers/blended_vector_layer.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/blended_vector_layer.ts rename to x-pack/plugins/maps/public/layers/blended_vector_layer.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/fields/ems_file_field.ts b/x-pack/plugins/maps/public/layers/fields/ems_file_field.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/fields/ems_file_field.ts rename to x-pack/plugins/maps/public/layers/fields/ems_file_field.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/fields/es_agg_field.test.ts b/x-pack/plugins/maps/public/layers/fields/es_agg_field.test.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/fields/es_agg_field.test.ts rename to x-pack/plugins/maps/public/layers/fields/es_agg_field.test.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/fields/es_agg_field.ts b/x-pack/plugins/maps/public/layers/fields/es_agg_field.ts similarity index 99% rename from x-pack/legacy/plugins/maps/public/layers/fields/es_agg_field.ts rename to x-pack/plugins/maps/public/layers/fields/es_agg_field.ts index 65f952ca01038c..34f7dd4b9578fc 100644 --- a/x-pack/legacy/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/legacy/plugins/maps/public/layers/fields/es_doc_field.ts b/x-pack/plugins/maps/public/layers/fields/es_doc_field.ts similarity index 96% rename from x-pack/legacy/plugins/maps/public/layers/fields/es_doc_field.ts rename to x-pack/plugins/maps/public/layers/fields/es_doc_field.ts index 4401452841a468..b7647d881fcf63 100644 --- a/x-pack/legacy/plugins/maps/public/layers/fields/es_doc_field.ts +++ b/x-pack/plugins/maps/public/layers/fields/es_doc_field.ts @@ -8,8 +8,8 @@ import { FIELD_ORIGIN } from '../../../common/constants'; import { ESTooltipProperty } from '../tooltips/es_tooltip_property'; import { ITooltipProperty, TooltipProperty } from '../tooltips/tooltip_property'; import { COLOR_PALETTE_MAX_SIZE } from '../../../common/constants'; -import { indexPatterns } from '../../../../../../../src/plugins/data/public'; -import { IFieldType } from '../../../../../../../src/plugins/data/public'; +import { indexPatterns } from '../../../../../../src/plugins/data/public'; +import { IFieldType } from '../../../../../../src/plugins/data/public'; import { IField, AbstractField } from './field'; import { IESSource } from '../sources/es_source'; import { IVectorSource } from '../sources/vector_source'; diff --git a/x-pack/legacy/plugins/maps/public/layers/fields/field.ts b/x-pack/plugins/maps/public/layers/fields/field.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/fields/field.ts rename to x-pack/plugins/maps/public/layers/fields/field.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/fields/kibana_region_field.ts b/x-pack/plugins/maps/public/layers/fields/kibana_region_field.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/fields/kibana_region_field.ts rename to x-pack/plugins/maps/public/layers/fields/kibana_region_field.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/fields/top_term_percentage_field.ts b/x-pack/plugins/maps/public/layers/fields/top_term_percentage_field.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/fields/top_term_percentage_field.ts rename to x-pack/plugins/maps/public/layers/fields/top_term_percentage_field.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/heatmap_layer.js b/x-pack/plugins/maps/public/layers/heatmap_layer.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/heatmap_layer.js rename to x-pack/plugins/maps/public/layers/heatmap_layer.js diff --git a/x-pack/legacy/plugins/maps/public/layers/joins/inner_join.js b/x-pack/plugins/maps/public/layers/joins/inner_join.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/joins/inner_join.js rename to x-pack/plugins/maps/public/layers/joins/inner_join.js diff --git a/x-pack/legacy/plugins/maps/public/layers/joins/inner_join.test.js b/x-pack/plugins/maps/public/layers/joins/inner_join.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/joins/inner_join.test.js rename to x-pack/plugins/maps/public/layers/joins/inner_join.test.js diff --git a/x-pack/legacy/plugins/maps/public/layers/joins/join.ts b/x-pack/plugins/maps/public/layers/joins/join.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/joins/join.ts rename to x-pack/plugins/maps/public/layers/joins/join.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/layer.d.ts b/x-pack/plugins/maps/public/layers/layer.d.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/layer.d.ts rename to x-pack/plugins/maps/public/layers/layer.d.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/layer.js b/x-pack/plugins/maps/public/layers/layer.js similarity index 99% rename from x-pack/legacy/plugins/maps/public/layers/layer.js rename to x-pack/plugins/maps/public/layers/layer.js index e9616be89b6015..26bce872b3c2c2 100644 --- a/x-pack/legacy/plugins/maps/public/layers/layer.js +++ b/x-pack/plugins/maps/public/layers/layer.js @@ -15,7 +15,7 @@ import { } from '../../common/constants'; import uuid from 'uuid/v4'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { copyPersistentState } from '../../../../../plugins/maps/public/reducers/util.js'; +import { copyPersistentState } from '../reducers/util.js'; import { i18n } from '@kbn/i18n'; export class AbstractLayer { diff --git a/x-pack/legacy/plugins/maps/public/layers/layer_wizard_registry.ts b/x-pack/plugins/maps/public/layers/layer_wizard_registry.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/layer_wizard_registry.ts rename to x-pack/plugins/maps/public/layers/layer_wizard_registry.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/load_layer_wizards.js b/x-pack/plugins/maps/public/layers/load_layer_wizards.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/load_layer_wizards.js rename to x-pack/plugins/maps/public/layers/load_layer_wizards.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/client_file_source/create_client_file_source_editor.js b/x-pack/plugins/maps/public/layers/sources/client_file_source/create_client_file_source_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/client_file_source/create_client_file_source_editor.js rename to x-pack/plugins/maps/public/layers/sources/client_file_source/create_client_file_source_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/client_file_source/geojson_file_source.js b/x-pack/plugins/maps/public/layers/sources/client_file_source/geojson_file_source.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/client_file_source/geojson_file_source.js rename to x-pack/plugins/maps/public/layers/sources/client_file_source/geojson_file_source.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/client_file_source/index.js b/x-pack/plugins/maps/public/layers/sources/client_file_source/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/client_file_source/index.js rename to x-pack/plugins/maps/public/layers/sources/client_file_source/index.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/create_source_editor.js b/x-pack/plugins/maps/public/layers/sources/ems_file_source/create_source_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/create_source_editor.js rename to x-pack/plugins/maps/public/layers/sources/ems_file_source/create_source_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.d.ts b/x-pack/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.d.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.d.ts rename to x-pack/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.d.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.js b/x-pack/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.js rename to x-pack/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.test.js b/x-pack/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.test.js rename to x-pack/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.test.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/index.js b/x-pack/plugins/maps/public/layers/sources/ems_file_source/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/index.js rename to x-pack/plugins/maps/public/layers/sources/ems_file_source/index.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/update_source_editor.js b/x-pack/plugins/maps/public/layers/sources/ems_file_source/update_source_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/update_source_editor.js rename to x-pack/plugins/maps/public/layers/sources/ems_file_source/update_source_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/ems_tms_source.js b/x-pack/plugins/maps/public/layers/sources/ems_tms_source/ems_tms_source.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/ems_tms_source.js rename to x-pack/plugins/maps/public/layers/sources/ems_tms_source/ems_tms_source.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/ems_tms_source.test.js b/x-pack/plugins/maps/public/layers/sources/ems_tms_source/ems_tms_source.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/ems_tms_source.test.js rename to x-pack/plugins/maps/public/layers/sources/ems_tms_source/ems_tms_source.test.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/index.js b/x-pack/plugins/maps/public/layers/sources/ems_tms_source/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/index.js rename to x-pack/plugins/maps/public/layers/sources/ems_tms_source/index.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/tile_service_select.js b/x-pack/plugins/maps/public/layers/sources/ems_tms_source/tile_service_select.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/tile_service_select.js rename to x-pack/plugins/maps/public/layers/sources/ems_tms_source/tile_service_select.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/update_source_editor.js b/x-pack/plugins/maps/public/layers/sources/ems_tms_source/update_source_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/update_source_editor.js rename to x-pack/plugins/maps/public/layers/sources/ems_tms_source/update_source_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/ems_unavailable_message.js b/x-pack/plugins/maps/public/layers/sources/ems_unavailable_message.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/ems_unavailable_message.js rename to x-pack/plugins/maps/public/layers/sources/ems_unavailable_message.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.d.ts b/x-pack/plugins/maps/public/layers/sources/es_agg_source.d.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.d.ts rename to x-pack/plugins/maps/public/layers/sources/es_agg_source.d.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.js b/x-pack/plugins/maps/public/layers/sources/es_agg_source.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.js rename to x-pack/plugins/maps/public/layers/sources/es_agg_source.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.test.ts b/x-pack/plugins/maps/public/layers/sources/es_agg_source.test.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_agg_source.test.ts rename to x-pack/plugins/maps/public/layers/sources/es_agg_source.test.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/convert_to_geojson.js b/x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/convert_to_geojson.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/convert_to_geojson.js rename to x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/convert_to_geojson.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/convert_to_geojson.test.ts b/x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/convert_to_geojson.test.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/convert_to_geojson.test.ts rename to x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/convert_to_geojson.test.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/create_source_editor.js b/x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/create_source_editor.js similarity index 97% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/create_source_editor.js rename to x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/create_source_editor.js index 4aec390bec745a..265606dc87e0fa 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/create_source_editor.js +++ b/x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/create_source_editor.js @@ -9,7 +9,6 @@ import React, { Fragment, Component } from 'react'; import PropTypes from 'prop-types'; import { SingleFieldSelect } from '../../../components/single_field_select'; -import { RENDER_AS } from '../../../../common/constants'; import { getIndexPatternService, getIndexPatternSelectComponent } from '../../../kibana_services'; import { NoIndexPatternCallout } from '../../../components/no_index_pattern_callout'; import { i18n } from '@kbn/i18n'; @@ -155,7 +154,7 @@ export class CreateSourceEditor extends Component { } _renderRenderAsSelect() { - if (this.state.requestType === RENDER_AS.HEATMAP || !this.state.indexPattern) { + if (!this.state.indexPattern) { return null; } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.d.ts b/x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.d.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.d.ts rename to x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.d.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js b/x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js similarity index 99% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js rename to x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js index dec802ac3cf1a7..04f944396ab35b 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js +++ b/x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js @@ -70,6 +70,12 @@ export class ESGeoGridSource extends AbstractESAggSource { ); } + getSyncMeta() { + return { + requestType: this._descriptor.requestType, + }; + } + async getImmutableProperties() { let indexPatternTitle = this.getIndexPatternId(); try { diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.test.ts b/x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.test.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.test.ts rename to x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.test.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/geo_tile_utils.js b/x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/geo_tile_utils.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/geo_tile_utils.js rename to x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/geo_tile_utils.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/geo_tile_utils.test.js b/x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/geo_tile_utils.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/geo_tile_utils.test.js rename to x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/geo_tile_utils.test.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/index.js b/x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/index.js rename to x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/index.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/render_as_select.tsx b/x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/render_as_select.tsx similarity index 89% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/render_as_select.tsx rename to x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/render_as_select.tsx index c82781ede186f2..899f4a797ea757 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/render_as_select.tsx +++ b/x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/render_as_select.tsx @@ -27,7 +27,12 @@ const options = [ export function RenderAsSelect(props: { renderAs: RENDER_AS; onChange: (newValue: RENDER_AS) => void; + isColumnCompressed?: boolean; }) { + if (props.renderAs === RENDER_AS.HEATMAP) { + return null; + } + function onChange(selectedOptions: Array>) { if (!selectedOptions || !selectedOptions.length) { return; @@ -46,6 +51,7 @@ export function RenderAsSelect(props: { label={i18n.translate('xpack.maps.source.esGeoGrid.showAsLabel', { defaultMessage: 'Show as', })} + display={props.isColumnCompressed ? 'columnCompressed' : 'row'} > ); diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/resolution_editor.js b/x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/resolution_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/resolution_editor.js rename to x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/resolution_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/update_source_editor.js b/x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/update_source_editor.js similarity index 89% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/update_source_editor.js rename to x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/update_source_editor.js index 269c2a8b8633aa..c0d6cba3a024a3 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/update_source_editor.js +++ b/x-pack/plugins/maps/public/layers/sources/es_geo_grid_source/update_source_editor.js @@ -14,7 +14,8 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiPanel, EuiSpacer, EuiTitle } from '@elastic/eui'; import { isMetricCountable } from '../../util/is_metric_countable'; -import { indexPatterns } from '../../../../../../../../src/plugins/data/public'; +import { indexPatterns } from '../../../../../../../src/plugins/data/public'; +import { RenderAsSelect } from './render_as_select'; export class UpdateSourceEditor extends Component { state = { @@ -65,6 +66,10 @@ export class UpdateSourceEditor extends Component { this.props.onChange({ propName: 'resolution', value: e }); }; + _onRequestTypeSelect = requestType => { + this.props.onChange({ propName: 'requestType', value: requestType }); + }; + _renderMetricsPanel() { const metricsFilter = this.props.renderAs === RENDER_AS.HEATMAP @@ -113,6 +118,11 @@ export class UpdateSourceEditor extends Component { resolution={this.props.resolution} onChange={this._onResolutionChange} /> + diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/convert_to_lines.js b/x-pack/plugins/maps/public/layers/sources/es_pew_pew_source/convert_to_lines.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/convert_to_lines.js rename to x-pack/plugins/maps/public/layers/sources/es_pew_pew_source/convert_to_lines.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/convert_to_lines.test.ts b/x-pack/plugins/maps/public/layers/sources/es_pew_pew_source/convert_to_lines.test.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/convert_to_lines.test.ts rename to x-pack/plugins/maps/public/layers/sources/es_pew_pew_source/convert_to_lines.test.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/create_source_editor.js b/x-pack/plugins/maps/public/layers/sources/es_pew_pew_source/create_source_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/create_source_editor.js rename to x-pack/plugins/maps/public/layers/sources/es_pew_pew_source/create_source_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js b/x-pack/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js similarity index 98% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js rename to x-pack/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js index da2b663746b9da..ea3a2d2fe634d5 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js +++ b/x-pack/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js @@ -25,7 +25,7 @@ import { convertToLines } from './convert_to_lines'; import { AbstractESAggSource } from '../es_agg_source'; import { DynamicStyleProperty } from '../../styles/vector/properties/dynamic_style_property'; import { COLOR_GRADIENTS } from '../../styles/color_utils'; -import { indexPatterns } from '../../../../../../../../src/plugins/data/public'; +import { indexPatterns } from '../../../../../../../src/plugins/data/public'; import { registerSource } from '../source_registry'; const MAX_GEOTILE_LEVEL = 29; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/update_source_editor.js b/x-pack/plugins/maps/public/layers/sources/es_pew_pew_source/update_source_editor.js similarity index 96% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/update_source_editor.js rename to x-pack/plugins/maps/public/layers/sources/es_pew_pew_source/update_source_editor.js index ce1f53c33ba53f..dea59a1c82f8aa 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/update_source_editor.js +++ b/x-pack/plugins/maps/public/layers/sources/es_pew_pew_source/update_source_editor.js @@ -11,7 +11,7 @@ import { getIndexPatternService } from '../../../kibana_services'; import { i18n } from '@kbn/i18n'; import { EuiPanel, EuiTitle, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { indexPatterns } from '../../../../../../../../src/plugins/data/public'; +import { indexPatterns } from '../../../../../../../src/plugins/data/public'; export class UpdateSourceEditor extends Component { state = { diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/__snapshots__/scaling_form.test.tsx.snap b/x-pack/plugins/maps/public/layers/sources/es_search_source/__snapshots__/scaling_form.test.tsx.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/__snapshots__/scaling_form.test.tsx.snap rename to x-pack/plugins/maps/public/layers/sources/es_search_source/__snapshots__/scaling_form.test.tsx.snap diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/__snapshots__/update_source_editor.test.js.snap b/x-pack/plugins/maps/public/layers/sources/es_search_source/__snapshots__/update_source_editor.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/__snapshots__/update_source_editor.test.js.snap rename to x-pack/plugins/maps/public/layers/sources/es_search_source/__snapshots__/update_source_editor.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/constants.js b/x-pack/plugins/maps/public/layers/sources/es_search_source/constants.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/constants.js rename to x-pack/plugins/maps/public/layers/sources/es_search_source/constants.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/create_source_editor.js b/x-pack/plugins/maps/public/layers/sources/es_search_source/create_source_editor.js similarity index 98% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/create_source_editor.js rename to x-pack/plugins/maps/public/layers/sources/es_search_source/create_source_editor.js index 73bea574ace288..aeb3835354f078 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/create_source_editor.js +++ b/x-pack/plugins/maps/public/layers/sources/es_search_source/create_source_editor.js @@ -15,7 +15,7 @@ import { NoIndexPatternCallout } from '../../../components/no_index_pattern_call import { i18n } from '@kbn/i18n'; import { ES_GEO_FIELD_TYPE, SCALING_TYPES } from '../../../../common/constants'; import { DEFAULT_FILTER_BY_MAP_BOUNDS } from './constants'; -import { indexPatterns } from '../../../../../../../../src/plugins/data/public'; +import { indexPatterns } from '../../../../../../../src/plugins/data/public'; import { ScalingForm } from './scaling_form'; import { getTermsFields } from '../../../index_pattern_util'; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.d.ts b/x-pack/plugins/maps/public/layers/sources/es_search_source/es_search_source.d.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.d.ts rename to x-pack/plugins/maps/public/layers/sources/es_search_source/es_search_source.d.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js b/x-pack/plugins/maps/public/layers/sources/es_search_source/es_search_source.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js rename to x-pack/plugins/maps/public/layers/sources/es_search_source/es_search_source.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.test.ts b/x-pack/plugins/maps/public/layers/sources/es_search_source/es_search_source.test.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.test.ts rename to x-pack/plugins/maps/public/layers/sources/es_search_source/es_search_source.test.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/index.js b/x-pack/plugins/maps/public/layers/sources/es_search_source/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/index.js rename to x-pack/plugins/maps/public/layers/sources/es_search_source/index.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/load_index_settings.js b/x-pack/plugins/maps/public/layers/sources/es_search_source/load_index_settings.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/load_index_settings.js rename to x-pack/plugins/maps/public/layers/sources/es_search_source/load_index_settings.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/scaling_form.test.tsx b/x-pack/plugins/maps/public/layers/sources/es_search_source/scaling_form.test.tsx similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/scaling_form.test.tsx rename to x-pack/plugins/maps/public/layers/sources/es_search_source/scaling_form.test.tsx diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/scaling_form.tsx b/x-pack/plugins/maps/public/layers/sources/es_search_source/scaling_form.tsx similarity index 97% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/scaling_form.tsx rename to x-pack/plugins/maps/public/layers/sources/es_search_source/scaling_form.tsx index c5950f1132974d..d86fc6d4026e60 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/scaling_form.tsx +++ b/x-pack/plugins/maps/public/layers/sources/es_search_source/scaling_form.tsx @@ -22,8 +22,6 @@ import { SingleFieldSelect } from '../../../components/single_field_select'; // @ts-ignore import { indexPatternService } from '../../../kibana_services'; // @ts-ignore -import { getTermsFields, getSourceFields } from '../../../index_pattern_util'; -// @ts-ignore import { ValidatedRange } from '../../../components/validated_range'; import { DEFAULT_MAX_INNER_RESULT_WINDOW, @@ -33,7 +31,7 @@ import { } from '../../../../common/constants'; // @ts-ignore import { loadIndexSettings } from './load_index_settings'; -import { IFieldType } from '../../../../../../../../src/plugins/data/public'; +import { IFieldType } from '../../../../../../../src/plugins/data/public'; import { OnSourceChangeArgs } from '../../../connected_components/layer_panel/view'; interface Props { diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.js b/x-pack/plugins/maps/public/layers/sources/es_search_source/update_source_editor.js similarity index 98% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.js rename to x-pack/plugins/maps/public/layers/sources/es_search_source/update_source_editor.js index 9c92ec5801e49e..cb6255afd0a42f 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.js +++ b/x-pack/plugins/maps/public/layers/sources/es_search_source/update_source_editor.js @@ -16,7 +16,7 @@ import { getTermsFields, getSourceFields } from '../../../index_pattern_util'; import { SORT_ORDER } from '../../../../common/constants'; import { ESDocField } from '../../fields/es_doc_field'; import { FormattedMessage } from '@kbn/i18n/react'; -import { indexPatterns } from '../../../../../../../../src/plugins/data/public'; +import { indexPatterns } from '../../../../../../../src/plugins/data/public'; import { ScalingForm } from './scaling_form'; export class UpdateSourceEditor extends Component { diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.test.js b/x-pack/plugins/maps/public/layers/sources/es_search_source/update_source_editor.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/update_source_editor.test.js rename to x-pack/plugins/maps/public/layers/sources/es_search_source/update_source_editor.test.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_source.d.ts b/x-pack/plugins/maps/public/layers/sources/es_source.d.ts similarity index 92% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_source.d.ts rename to x-pack/plugins/maps/public/layers/sources/es_source.d.ts index ffd1d343b59e07..65851d0e7bd386 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_source.d.ts +++ b/x-pack/plugins/maps/public/layers/sources/es_source.d.ts @@ -6,7 +6,7 @@ import { AbstractVectorSource } from './vector_source'; import { IVectorSource } from './vector_source'; -import { IndexPattern, SearchSource } from '../../../../../../../src/plugins/data/public'; +import { IndexPattern, SearchSource } from '../../../../../../src/plugins/data/public'; import { VectorSourceRequestMeta } from '../../../common/descriptor_types'; export interface IESSource extends IVectorSource { diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js b/x-pack/plugins/maps/public/layers/sources/es_source.js similarity index 99% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_source.js rename to x-pack/plugins/maps/public/layers/sources/es_source.js index 441d52d23398a0..d90a802a38344b 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js +++ b/x-pack/plugins/maps/public/layers/sources/es_source.js @@ -17,7 +17,7 @@ import _ from 'lodash'; import { i18n } from '@kbn/i18n'; import uuid from 'uuid/v4'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { copyPersistentState } from '../../../../../../plugins/maps/public/reducers/util'; +import { copyPersistentState } from '../../reducers/util'; import { ES_GEO_FIELD_TYPE } from '../../../common/constants'; import { DataRequestAbortError } from '../util/data_request'; import { expandToTileBoundaries } from './es_geo_grid_source/geo_tile_utils'; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.d.ts b/x-pack/plugins/maps/public/layers/sources/es_term_source.d.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.d.ts rename to x-pack/plugins/maps/public/layers/sources/es_term_source.d.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.js b/x-pack/plugins/maps/public/layers/sources/es_term_source.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.js rename to x-pack/plugins/maps/public/layers/sources/es_term_source.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.test.js b/x-pack/plugins/maps/public/layers/sources/es_term_source.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/es_term_source.test.js rename to x-pack/plugins/maps/public/layers/sources/es_term_source.test.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/kibana_regionmap_source/create_source_editor.js b/x-pack/plugins/maps/public/layers/sources/kibana_regionmap_source/create_source_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/kibana_regionmap_source/create_source_editor.js rename to x-pack/plugins/maps/public/layers/sources/kibana_regionmap_source/create_source_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/kibana_regionmap_source/index.js b/x-pack/plugins/maps/public/layers/sources/kibana_regionmap_source/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/kibana_regionmap_source/index.js rename to x-pack/plugins/maps/public/layers/sources/kibana_regionmap_source/index.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/kibana_regionmap_source/kibana_regionmap_source.d.ts b/x-pack/plugins/maps/public/layers/sources/kibana_regionmap_source/kibana_regionmap_source.d.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/kibana_regionmap_source/kibana_regionmap_source.d.ts rename to x-pack/plugins/maps/public/layers/sources/kibana_regionmap_source/kibana_regionmap_source.d.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/kibana_regionmap_source/kibana_regionmap_source.js b/x-pack/plugins/maps/public/layers/sources/kibana_regionmap_source/kibana_regionmap_source.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/kibana_regionmap_source/kibana_regionmap_source.js rename to x-pack/plugins/maps/public/layers/sources/kibana_regionmap_source/kibana_regionmap_source.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/kibana_tilemap_source/create_source_editor.js b/x-pack/plugins/maps/public/layers/sources/kibana_tilemap_source/create_source_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/kibana_tilemap_source/create_source_editor.js rename to x-pack/plugins/maps/public/layers/sources/kibana_tilemap_source/create_source_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/kibana_tilemap_source/index.js b/x-pack/plugins/maps/public/layers/sources/kibana_tilemap_source/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/kibana_tilemap_source/index.js rename to x-pack/plugins/maps/public/layers/sources/kibana_tilemap_source/index.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/kibana_tilemap_source/kibana_tilemap_source.js b/x-pack/plugins/maps/public/layers/sources/kibana_tilemap_source/kibana_tilemap_source.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/kibana_tilemap_source/kibana_tilemap_source.js rename to x-pack/plugins/maps/public/layers/sources/kibana_tilemap_source/kibana_tilemap_source.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/source.d.ts b/x-pack/plugins/maps/public/layers/sources/source.d.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/source.d.ts rename to x-pack/plugins/maps/public/layers/sources/source.d.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/source.js b/x-pack/plugins/maps/public/layers/sources/source.js similarity index 95% rename from x-pack/legacy/plugins/maps/public/layers/sources/source.js rename to x-pack/plugins/maps/public/layers/sources/source.js index b6b6c10831bb5d..3029a5c0912026 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/source.js +++ b/x-pack/plugins/maps/public/layers/sources/source.js @@ -5,7 +5,7 @@ */ // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { copyPersistentState } from '../../../../../../plugins/maps/public/reducers/util'; +import { copyPersistentState } from '../../reducers/util'; export class AbstractSource { static isIndexingSource = false; @@ -111,10 +111,6 @@ export class AbstractSource { return 0; } - getSyncMeta() { - return {}; - } - isJoinable() { return false; } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/source_registry.ts b/x-pack/plugins/maps/public/layers/sources/source_registry.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/source_registry.ts rename to x-pack/plugins/maps/public/layers/sources/source_registry.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/tms_source.d.ts b/x-pack/plugins/maps/public/layers/sources/tms_source.d.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/tms_source.d.ts rename to x-pack/plugins/maps/public/layers/sources/tms_source.d.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/tms_source.js b/x-pack/plugins/maps/public/layers/sources/tms_source.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/tms_source.js rename to x-pack/plugins/maps/public/layers/sources/tms_source.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/vector_feature_types.js b/x-pack/plugins/maps/public/layers/sources/vector_feature_types.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/vector_feature_types.js rename to x-pack/plugins/maps/public/layers/sources/vector_feature_types.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.d.ts b/x-pack/plugins/maps/public/layers/sources/vector_source.d.ts similarity index 93% rename from x-pack/legacy/plugins/maps/public/layers/sources/vector_source.d.ts rename to x-pack/plugins/maps/public/layers/sources/vector_source.d.ts index 1400654297e011..d597e642771866 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.d.ts +++ b/x-pack/plugins/maps/public/layers/sources/vector_source.d.ts @@ -12,6 +12,7 @@ import { ESSearchSourceResponseMeta, MapExtent, VectorSourceRequestMeta, + VectorSourceSyncMeta, } from '../../../common/descriptor_types'; export type GeoJsonFetchMeta = ESSearchSourceResponseMeta; @@ -31,6 +32,7 @@ export interface IVectorSource extends ISource { getFields(): Promise; getFieldByName(fieldName: string): IField; + getSyncMeta(): VectorSourceSyncMeta; } export class AbstractVectorSource extends AbstractSource implements IVectorSource { @@ -43,4 +45,5 @@ export class AbstractVectorSource extends AbstractSource implements IVectorSourc getFields(): Promise; getFieldByName(fieldName: string): IField; + getSyncMeta(): VectorSourceSyncMeta; } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js b/x-pack/plugins/maps/public/layers/sources/vector_source.js similarity index 99% rename from x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js rename to x-pack/plugins/maps/public/layers/sources/vector_source.js index 7ff1c735c8613a..7f97b1b21d189a 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/vector_source.js +++ b/x-pack/plugins/maps/public/layers/sources/vector_source.js @@ -151,4 +151,8 @@ export class AbstractVectorSource extends AbstractSource { getSourceTooltipContent(/* sourceDataRequest */) { return { tooltipContent: null, areResultsTrimmed: false }; } + + getSyncMeta() { + return {}; + } } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/wms_source/index.js b/x-pack/plugins/maps/public/layers/sources/wms_source/index.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/wms_source/index.js rename to x-pack/plugins/maps/public/layers/sources/wms_source/index.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/wms_source/wms_client.js b/x-pack/plugins/maps/public/layers/sources/wms_source/wms_client.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/wms_source/wms_client.js rename to x-pack/plugins/maps/public/layers/sources/wms_source/wms_client.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/wms_source/wms_client.test.js b/x-pack/plugins/maps/public/layers/sources/wms_source/wms_client.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/wms_source/wms_client.test.js rename to x-pack/plugins/maps/public/layers/sources/wms_source/wms_client.test.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/wms_source/wms_create_source_editor.js b/x-pack/plugins/maps/public/layers/sources/wms_source/wms_create_source_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/wms_source/wms_create_source_editor.js rename to x-pack/plugins/maps/public/layers/sources/wms_source/wms_create_source_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/wms_source/wms_source.js b/x-pack/plugins/maps/public/layers/sources/wms_source/wms_source.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/wms_source/wms_source.js rename to x-pack/plugins/maps/public/layers/sources/wms_source/wms_source.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/xyz_tms_source.d.ts b/x-pack/plugins/maps/public/layers/sources/xyz_tms_source.d.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/xyz_tms_source.d.ts rename to x-pack/plugins/maps/public/layers/sources/xyz_tms_source.d.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/xyz_tms_source.js b/x-pack/plugins/maps/public/layers/sources/xyz_tms_source.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/xyz_tms_source.js rename to x-pack/plugins/maps/public/layers/sources/xyz_tms_source.js diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/xyz_tms_source.test.ts b/x-pack/plugins/maps/public/layers/sources/xyz_tms_source.test.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/sources/xyz_tms_source.test.ts rename to x-pack/plugins/maps/public/layers/sources/xyz_tms_source.test.ts diff --git a/x-pack/plugins/maps/public/layers/styles/_index.scss b/x-pack/plugins/maps/public/layers/styles/_index.scss new file mode 100644 index 00000000000000..a1c4c297a3ac18 --- /dev/null +++ b/x-pack/plugins/maps/public/layers/styles/_index.scss @@ -0,0 +1,4 @@ +@import 'components/color_gradient'; +@import 'vector/components/style_prop_editor'; +@import 'vector/components/color/color_stops'; +@import 'vector/components/symbol/icon_select'; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/abstract_style.js b/x-pack/plugins/maps/public/layers/styles/abstract_style.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/abstract_style.js rename to x-pack/plugins/maps/public/layers/styles/abstract_style.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js b/x-pack/plugins/maps/public/layers/styles/color_utils.js similarity index 98% rename from x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js rename to x-pack/plugins/maps/public/layers/styles/color_utils.js index 09c7d76db1691a..23b61b07bf8711 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.js +++ b/x-pack/plugins/maps/public/layers/styles/color_utils.js @@ -10,7 +10,7 @@ import chroma from 'chroma-js'; import { euiPaletteColorBlind } from '@elastic/eui/lib/services'; import { ColorGradient } from './components/color_gradient'; import { COLOR_PALETTE_MAX_SIZE } from '../../../common/constants'; -import { vislibColorMaps } from '../../../../../../../src/plugins/charts/public'; +import { vislibColorMaps } from '../../../../../../src/plugins/charts/public'; const GRADIENT_INTERVALS = 8; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/color_utils.test.js b/x-pack/plugins/maps/public/layers/styles/color_utils.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/color_utils.test.js rename to x-pack/plugins/maps/public/layers/styles/color_utils.test.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/_color_gradient.scss b/x-pack/plugins/maps/public/layers/styles/components/_color_gradient.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/_color_gradient.scss rename to x-pack/plugins/maps/public/layers/styles/components/_color_gradient.scss diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/color_gradient.js b/x-pack/plugins/maps/public/layers/styles/components/color_gradient.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/color_gradient.js rename to x-pack/plugins/maps/public/layers/styles/components/color_gradient.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/components/ranged_style_legend_row.js b/x-pack/plugins/maps/public/layers/styles/components/ranged_style_legend_row.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/components/ranged_style_legend_row.js rename to x-pack/plugins/maps/public/layers/styles/components/ranged_style_legend_row.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/__snapshots__/heatmap_style_editor.test.js.snap b/x-pack/plugins/maps/public/layers/styles/heatmap/components/__snapshots__/heatmap_style_editor.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/__snapshots__/heatmap_style_editor.test.js.snap rename to x-pack/plugins/maps/public/layers/styles/heatmap/components/__snapshots__/heatmap_style_editor.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/heatmap_constants.js b/x-pack/plugins/maps/public/layers/styles/heatmap/components/heatmap_constants.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/heatmap_constants.js rename to x-pack/plugins/maps/public/layers/styles/heatmap/components/heatmap_constants.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/heatmap_style_editor.js b/x-pack/plugins/maps/public/layers/styles/heatmap/components/heatmap_style_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/heatmap_style_editor.js rename to x-pack/plugins/maps/public/layers/styles/heatmap/components/heatmap_style_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/heatmap_style_editor.test.js b/x-pack/plugins/maps/public/layers/styles/heatmap/components/heatmap_style_editor.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/heatmap_style_editor.test.js rename to x-pack/plugins/maps/public/layers/styles/heatmap/components/heatmap_style_editor.test.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/legend/heatmap_legend.js b/x-pack/plugins/maps/public/layers/styles/heatmap/components/legend/heatmap_legend.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/heatmap/components/legend/heatmap_legend.js rename to x-pack/plugins/maps/public/layers/styles/heatmap/components/legend/heatmap_legend.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/heatmap/heatmap_style.js b/x-pack/plugins/maps/public/layers/styles/heatmap/heatmap_style.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/heatmap/heatmap_style.js rename to x-pack/plugins/maps/public/layers/styles/heatmap/heatmap_style.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/_style_prop_editor.scss b/x-pack/plugins/maps/public/layers/styles/vector/components/_style_prop_editor.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/_style_prop_editor.scss rename to x-pack/plugins/maps/public/layers/styles/vector/components/_style_prop_editor.scss diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/_color_stops.scss b/x-pack/plugins/maps/public/layers/styles/vector/components/color/_color_stops.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/_color_stops.scss rename to x-pack/plugins/maps/public/layers/styles/vector/components/color/_color_stops.scss diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_map_select.js b/x-pack/plugins/maps/public/layers/styles/vector/components/color/color_map_select.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_map_select.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/color/color_map_select.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops.js b/x-pack/plugins/maps/public/layers/styles/vector/components/color/color_stops.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/color/color_stops.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js b/x-pack/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/color/color_stops_categorical.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_ordinal.js b/x-pack/plugins/maps/public/layers/styles/vector/components/color/color_stops_ordinal.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_ordinal.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/color/color_stops_ordinal.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_utils.js b/x-pack/plugins/maps/public/layers/styles/vector/components/color/color_stops_utils.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/color_stops_utils.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/color/color_stops_utils.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_form.js b/x-pack/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_form.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_form.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/color/dynamic_color_form.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/mb_validated_color_picker.tsx b/x-pack/plugins/maps/public/layers/styles/vector/components/color/mb_validated_color_picker.tsx similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/mb_validated_color_picker.tsx rename to x-pack/plugins/maps/public/layers/styles/vector/components/color/mb_validated_color_picker.tsx diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/static_color_form.js b/x-pack/plugins/maps/public/layers/styles/vector/components/color/static_color_form.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/static_color_form.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/color/static_color_form.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/vector_style_color_editor.js b/x-pack/plugins/maps/public/layers/styles/vector/components/color/vector_style_color_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/color/vector_style_color_editor.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/color/vector_style_color_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/field_meta/categorical_field_meta_popover.tsx b/x-pack/plugins/maps/public/layers/styles/vector/components/field_meta/categorical_field_meta_popover.tsx similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/field_meta/categorical_field_meta_popover.tsx rename to x-pack/plugins/maps/public/layers/styles/vector/components/field_meta/categorical_field_meta_popover.tsx diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/field_meta/field_meta_popover.tsx b/x-pack/plugins/maps/public/layers/styles/vector/components/field_meta/field_meta_popover.tsx similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/field_meta/field_meta_popover.tsx rename to x-pack/plugins/maps/public/layers/styles/vector/components/field_meta/field_meta_popover.tsx diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/field_meta/ordinal_field_meta_popover.tsx b/x-pack/plugins/maps/public/layers/styles/vector/components/field_meta/ordinal_field_meta_popover.tsx similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/field_meta/ordinal_field_meta_popover.tsx rename to x-pack/plugins/maps/public/layers/styles/vector/components/field_meta/ordinal_field_meta_popover.tsx diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/field_select.js b/x-pack/plugins/maps/public/layers/styles/vector/components/field_select.js similarity index 97% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/field_select.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/field_select.js index 2f5de507657a5b..ed2e7a4eab7ecc 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/field_select.js +++ b/x-pack/plugins/maps/public/layers/styles/vector/components/field_select.js @@ -10,7 +10,7 @@ import React from 'react'; import { EuiComboBox, EuiHighlight, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { FIELD_ORIGIN } from '../../../../../common/constants'; import { i18n } from '@kbn/i18n'; -import { FieldIcon } from '../../../../../../../../../src/plugins/kibana_react/public'; +import { FieldIcon } from '../../../../../../../../src/plugins/kibana_react/public'; function renderOption(option, searchValue, contentClassName) { return ( diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/get_vector_style_label.js b/x-pack/plugins/maps/public/layers/styles/vector/components/get_vector_style_label.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/get_vector_style_label.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/get_vector_style_label.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/dynamic_label_form.js b/x-pack/plugins/maps/public/layers/styles/vector/components/label/dynamic_label_form.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/dynamic_label_form.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/label/dynamic_label_form.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/static_label_form.js b/x-pack/plugins/maps/public/layers/styles/vector/components/label/static_label_form.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/static_label_form.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/label/static_label_form.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/vector_style_label_border_size_editor.js b/x-pack/plugins/maps/public/layers/styles/vector/components/label/vector_style_label_border_size_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/vector_style_label_border_size_editor.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/label/vector_style_label_border_size_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/vector_style_label_editor.js b/x-pack/plugins/maps/public/layers/styles/vector/components/label/vector_style_label_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/label/vector_style_label_editor.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/label/vector_style_label_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/__snapshots__/vector_icon.test.js.snap b/x-pack/plugins/maps/public/layers/styles/vector/components/legend/__snapshots__/vector_icon.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/__snapshots__/vector_icon.test.js.snap rename to x-pack/plugins/maps/public/layers/styles/vector/components/legend/__snapshots__/vector_icon.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/category.js b/x-pack/plugins/maps/public/layers/styles/vector/components/legend/category.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/category.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/legend/category.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/circle_icon.js b/x-pack/plugins/maps/public/layers/styles/vector/components/legend/circle_icon.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/circle_icon.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/legend/circle_icon.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/extract_color_from_style_property.js b/x-pack/plugins/maps/public/layers/styles/vector/components/legend/extract_color_from_style_property.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/extract_color_from_style_property.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/legend/extract_color_from_style_property.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/line_icon.js b/x-pack/plugins/maps/public/layers/styles/vector/components/legend/line_icon.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/line_icon.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/legend/line_icon.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/polygon_icon.js b/x-pack/plugins/maps/public/layers/styles/vector/components/legend/polygon_icon.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/polygon_icon.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/legend/polygon_icon.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/symbol_icon.js b/x-pack/plugins/maps/public/layers/styles/vector/components/legend/symbol_icon.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/symbol_icon.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/legend/symbol_icon.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_icon.js b/x-pack/plugins/maps/public/layers/styles/vector/components/legend/vector_icon.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_icon.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/legend/vector_icon.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_icon.test.js b/x-pack/plugins/maps/public/layers/styles/vector/components/legend/vector_icon.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_icon.test.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/legend/vector_icon.test.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_style_legend.js b/x-pack/plugins/maps/public/layers/styles/vector/components/legend/vector_style_legend.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/legend/vector_style_legend.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/legend/vector_style_legend.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/dynamic_orientation_form.js b/x-pack/plugins/maps/public/layers/styles/vector/components/orientation/dynamic_orientation_form.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/dynamic_orientation_form.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/orientation/dynamic_orientation_form.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/orientation_editor.js b/x-pack/plugins/maps/public/layers/styles/vector/components/orientation/orientation_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/orientation_editor.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/orientation/orientation_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/static_orientation_form.js b/x-pack/plugins/maps/public/layers/styles/vector/components/orientation/static_orientation_form.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/orientation/static_orientation_form.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/orientation/static_orientation_form.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/dynamic_size_form.js b/x-pack/plugins/maps/public/layers/styles/vector/components/size/dynamic_size_form.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/dynamic_size_form.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/size/dynamic_size_form.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/size_range_selector.js b/x-pack/plugins/maps/public/layers/styles/vector/components/size/size_range_selector.js similarity index 92% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/size_range_selector.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/size/size_range_selector.js index 5de7b462136e16..ec847e2a5384e0 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/size_range_selector.js +++ b/x-pack/plugins/maps/public/layers/styles/vector/components/size/size_range_selector.js @@ -6,7 +6,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { ValidatedDualRange } from '../../../../../../../../../../src/plugins/kibana_react/public'; +import { ValidatedDualRange } from '../../../../../../../../../src/plugins/kibana_react/public'; import { MIN_SIZE, MAX_SIZE } from '../../vector_style_defaults'; import { i18n } from '@kbn/i18n'; diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/static_size_form.js b/x-pack/plugins/maps/public/layers/styles/vector/components/size/static_size_form.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/static_size_form.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/size/static_size_form.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/vector_style_size_editor.js b/x-pack/plugins/maps/public/layers/styles/vector/components/size/vector_style_size_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/size/vector_style_size_editor.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/size/vector_style_size_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js b/x-pack/plugins/maps/public/layers/styles/vector/components/stop_input.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/stop_input.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/stop_input.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_map_select.js b/x-pack/plugins/maps/public/layers/styles/vector/components/style_map_select.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_map_select.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/style_map_select.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_option_shapes.js b/x-pack/plugins/maps/public/layers/styles/vector/components/style_option_shapes.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_option_shapes.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/style_option_shapes.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_prop_editor.js b/x-pack/plugins/maps/public/layers/styles/vector/components/style_prop_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/style_prop_editor.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/style_prop_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/__snapshots__/icon_select.test.js.snap b/x-pack/plugins/maps/public/layers/styles/vector/components/symbol/__snapshots__/icon_select.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/__snapshots__/icon_select.test.js.snap rename to x-pack/plugins/maps/public/layers/styles/vector/components/symbol/__snapshots__/icon_select.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/_icon_select.scss b/x-pack/plugins/maps/public/layers/styles/vector/components/symbol/_icon_select.scss similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/_icon_select.scss rename to x-pack/plugins/maps/public/layers/styles/vector/components/symbol/_icon_select.scss diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/dynamic_icon_form.js b/x-pack/plugins/maps/public/layers/styles/vector/components/symbol/dynamic_icon_form.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/dynamic_icon_form.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/symbol/dynamic_icon_form.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/icon_map_select.js b/x-pack/plugins/maps/public/layers/styles/vector/components/symbol/icon_map_select.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/icon_map_select.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/symbol/icon_map_select.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/icon_select.js b/x-pack/plugins/maps/public/layers/styles/vector/components/symbol/icon_select.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/icon_select.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/symbol/icon_select.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/icon_select.test.js b/x-pack/plugins/maps/public/layers/styles/vector/components/symbol/icon_select.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/icon_select.test.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/symbol/icon_select.test.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/icon_stops.js b/x-pack/plugins/maps/public/layers/styles/vector/components/symbol/icon_stops.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/icon_stops.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/symbol/icon_stops.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/icon_stops.test.js b/x-pack/plugins/maps/public/layers/styles/vector/components/symbol/icon_stops.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/icon_stops.test.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/symbol/icon_stops.test.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/static_icon_form.js b/x-pack/plugins/maps/public/layers/styles/vector/components/symbol/static_icon_form.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/static_icon_form.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/symbol/static_icon_form.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/vector_style_icon_editor.js b/x-pack/plugins/maps/public/layers/styles/vector/components/symbol/vector_style_icon_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/vector_style_icon_editor.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/symbol/vector_style_icon_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/vector_style_symbolize_as_editor.js b/x-pack/plugins/maps/public/layers/styles/vector/components/symbol/vector_style_symbolize_as_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/symbol/vector_style_symbolize_as_editor.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/symbol/vector_style_symbolize_as_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js b/x-pack/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js rename to x-pack/plugins/maps/public/layers/styles/vector/components/vector_style_editor.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/__snapshots__/dynamic_color_property.test.js.snap b/x-pack/plugins/maps/public/layers/styles/vector/properties/__snapshots__/dynamic_color_property.test.js.snap similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/__snapshots__/dynamic_color_property.test.js.snap rename to x-pack/plugins/maps/public/layers/styles/vector/properties/__snapshots__/dynamic_color_property.test.js.snap diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/components/categorical_legend.js b/x-pack/plugins/maps/public/layers/styles/vector/properties/components/categorical_legend.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/components/categorical_legend.js rename to x-pack/plugins/maps/public/layers/styles/vector/properties/components/categorical_legend.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/components/ordinal_legend.js b/x-pack/plugins/maps/public/layers/styles/vector/properties/components/ordinal_legend.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/components/ordinal_legend.js rename to x-pack/plugins/maps/public/layers/styles/vector/properties/components/ordinal_legend.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js b/x-pack/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js rename to x-pack/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.test.js b/x-pack/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.test.js rename to x-pack/plugins/maps/public/layers/styles/vector/properties/dynamic_color_property.test.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_icon_property.js b/x-pack/plugins/maps/public/layers/styles/vector/properties/dynamic_icon_property.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_icon_property.js rename to x-pack/plugins/maps/public/layers/styles/vector/properties/dynamic_icon_property.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_orientation_property.js b/x-pack/plugins/maps/public/layers/styles/vector/properties/dynamic_orientation_property.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_orientation_property.js rename to x-pack/plugins/maps/public/layers/styles/vector/properties/dynamic_orientation_property.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_size_property.js b/x-pack/plugins/maps/public/layers/styles/vector/properties/dynamic_size_property.js similarity index 95% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_size_property.js rename to x-pack/plugins/maps/public/layers/styles/vector/properties/dynamic_size_property.js index 8b3f670bfa5280..71ac25f0c6e619 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_size_property.js +++ b/x-pack/plugins/maps/public/layers/styles/vector/properties/dynamic_size_property.js @@ -99,9 +99,13 @@ export class DynamicSizeProperty extends DynamicStyleProperty { } } - syncCircleStrokeWidthWithMb(mbLayerId, mbMap) { - const lineWidth = this.getMbSizeExpression(); - mbMap.setPaintProperty(mbLayerId, 'circle-stroke-width', lineWidth); + syncCircleStrokeWidthWithMb(mbLayerId, mbMap, hasNoRadius) { + if (hasNoRadius) { + mbMap.setPaintProperty(mbLayerId, 'circle-stroke-width', 0); + } else { + const lineWidth = this.getMbSizeExpression(); + mbMap.setPaintProperty(mbLayerId, 'circle-stroke-width', lineWidth); + } } syncCircleRadiusWithMb(mbLayerId, mbMap) { diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.d.ts b/x-pack/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.d.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.d.ts rename to x-pack/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.d.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js b/x-pack/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js rename to x-pack/plugins/maps/public/layers/styles/vector/properties/dynamic_style_property.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_text_property.js b/x-pack/plugins/maps/public/layers/styles/vector/properties/dynamic_text_property.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/dynamic_text_property.js rename to x-pack/plugins/maps/public/layers/styles/vector/properties/dynamic_text_property.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/label_border_size_property.js b/x-pack/plugins/maps/public/layers/styles/vector/properties/label_border_size_property.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/label_border_size_property.js rename to x-pack/plugins/maps/public/layers/styles/vector/properties/label_border_size_property.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_color_property.js b/x-pack/plugins/maps/public/layers/styles/vector/properties/static_color_property.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_color_property.js rename to x-pack/plugins/maps/public/layers/styles/vector/properties/static_color_property.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_icon_property.js b/x-pack/plugins/maps/public/layers/styles/vector/properties/static_icon_property.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_icon_property.js rename to x-pack/plugins/maps/public/layers/styles/vector/properties/static_icon_property.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_orientation_property.js b/x-pack/plugins/maps/public/layers/styles/vector/properties/static_orientation_property.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_orientation_property.js rename to x-pack/plugins/maps/public/layers/styles/vector/properties/static_orientation_property.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_size_property.js b/x-pack/plugins/maps/public/layers/styles/vector/properties/static_size_property.js similarity index 85% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_size_property.js rename to x-pack/plugins/maps/public/layers/styles/vector/properties/static_size_property.js index 2383a5932cb9b1..d86556c6218cf8 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_size_property.js +++ b/x-pack/plugins/maps/public/layers/styles/vector/properties/static_size_property.js @@ -35,8 +35,12 @@ export class StaticSizeProperty extends StaticStyleProperty { mbMap.setLayoutProperty(symbolLayerId, 'icon-size', this._options.size / halfIconPixels); } - syncCircleStrokeWidthWithMb(mbLayerId, mbMap) { - mbMap.setPaintProperty(mbLayerId, 'circle-stroke-width', this._options.size); + syncCircleStrokeWidthWithMb(mbLayerId, mbMap, hasNoRadius) { + if (hasNoRadius) { + mbMap.setPaintProperty(mbLayerId, 'circle-stroke-width', 0); + } else { + mbMap.setPaintProperty(mbLayerId, 'circle-stroke-width', this._options.size); + } } syncCircleRadiusWithMb(mbLayerId, mbMap) { diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_style_property.js b/x-pack/plugins/maps/public/layers/styles/vector/properties/static_style_property.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_style_property.js rename to x-pack/plugins/maps/public/layers/styles/vector/properties/static_style_property.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_text_property.js b/x-pack/plugins/maps/public/layers/styles/vector/properties/static_text_property.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/static_text_property.js rename to x-pack/plugins/maps/public/layers/styles/vector/properties/static_text_property.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/style_property.ts b/x-pack/plugins/maps/public/layers/styles/vector/properties/style_property.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/style_property.ts rename to x-pack/plugins/maps/public/layers/styles/vector/properties/style_property.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/symbolize_as_property.js b/x-pack/plugins/maps/public/layers/styles/vector/properties/symbolize_as_property.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/properties/symbolize_as_property.js rename to x-pack/plugins/maps/public/layers/styles/vector/properties/symbolize_as_property.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/style_meta.ts b/x-pack/plugins/maps/public/layers/styles/vector/style_meta.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/style_meta.ts rename to x-pack/plugins/maps/public/layers/styles/vector/style_meta.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/style_util.js b/x-pack/plugins/maps/public/layers/styles/vector/style_util.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/style_util.js rename to x-pack/plugins/maps/public/layers/styles/vector/style_util.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/style_util.test.js b/x-pack/plugins/maps/public/layers/styles/vector/style_util.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/style_util.test.js rename to x-pack/plugins/maps/public/layers/styles/vector/style_util.test.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/symbol_utils.js b/x-pack/plugins/maps/public/layers/styles/vector/symbol_utils.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/symbol_utils.js rename to x-pack/plugins/maps/public/layers/styles/vector/symbol_utils.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/symbol_utils.test.js b/x-pack/plugins/maps/public/layers/styles/vector/symbol_utils.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/symbol_utils.test.js rename to x-pack/plugins/maps/public/layers/styles/vector/symbol_utils.test.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.d.ts b/x-pack/plugins/maps/public/layers/styles/vector/vector_style.d.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.d.ts rename to x-pack/plugins/maps/public/layers/styles/vector/vector_style.d.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js b/x-pack/plugins/maps/public/layers/styles/vector/vector_style.js similarity index 99% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js rename to x-pack/plugins/maps/public/layers/styles/vector/vector_style.js index ae5d148e43cfd8..b044c98d44d417 100644 --- a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.js +++ b/x-pack/plugins/maps/public/layers/styles/vector/vector_style.js @@ -543,8 +543,11 @@ export class VectorStyle extends AbstractStyle { setMBPaintPropertiesForPoints({ alpha, mbMap, pointLayerId }) { this._fillColorStyleProperty.syncCircleColorWithMb(pointLayerId, mbMap, alpha); this._lineColorStyleProperty.syncCircleStrokeWithMb(pointLayerId, mbMap, alpha); - this._lineWidthStyleProperty.syncCircleStrokeWidthWithMb(pointLayerId, mbMap); - this._iconSizeStyleProperty.syncCircleRadiusWithMb(pointLayerId, mbMap); + const hasNoRadius = + !this._iconSizeStyleProperty.isDynamic() && + this._iconSizeStyleProperty.getOptions().size === 0; + this._lineWidthStyleProperty.syncCircleStrokeWidthWithMb(pointLayerId, mbMap, hasNoRadius); + this._iconSizeStyleProperty.syncCircleRadiusWithMb(pointLayerId, mbMap, hasNoRadius); } setMBPropertiesForLabelText({ alpha, mbMap, textLayerId }) { diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.test.js b/x-pack/plugins/maps/public/layers/styles/vector/vector_style.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style.test.js rename to x-pack/plugins/maps/public/layers/styles/vector/vector_style.test.js diff --git a/x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style_defaults.ts b/x-pack/plugins/maps/public/layers/styles/vector/vector_style_defaults.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/styles/vector/vector_style_defaults.ts rename to x-pack/plugins/maps/public/layers/styles/vector/vector_style_defaults.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/tile_layer.d.ts b/x-pack/plugins/maps/public/layers/tile_layer.d.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/tile_layer.d.ts rename to x-pack/plugins/maps/public/layers/tile_layer.d.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/tile_layer.js b/x-pack/plugins/maps/public/layers/tile_layer.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/tile_layer.js rename to x-pack/plugins/maps/public/layers/tile_layer.js diff --git a/x-pack/legacy/plugins/maps/public/layers/tile_layer.test.ts b/x-pack/plugins/maps/public/layers/tile_layer.test.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/tile_layer.test.ts rename to x-pack/plugins/maps/public/layers/tile_layer.test.ts 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 new file mode 100644 index 00000000000000..acd05475f97627 --- /dev/null +++ b/x-pack/plugins/maps/public/layers/tooltips/es_agg_tooltip_property.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { 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 this._aggType === AGG_TYPE.TERMS; + } +} diff --git a/x-pack/legacy/plugins/maps/public/layers/tooltips/es_tooltip_property.ts b/x-pack/plugins/maps/public/layers/tooltips/es_tooltip_property.ts similarity index 95% rename from x-pack/legacy/plugins/maps/public/layers/tooltips/es_tooltip_property.ts rename to x-pack/plugins/maps/public/layers/tooltips/es_tooltip_property.ts index 8fd7e173435ceb..5c35009881920a 100644 --- a/x-pack/legacy/plugins/maps/public/layers/tooltips/es_tooltip_property.ts +++ b/x-pack/plugins/maps/public/layers/tooltips/es_tooltip_property.ts @@ -7,8 +7,8 @@ import _ from 'lodash'; import { ITooltipProperty } from './tooltip_property'; import { IField } from '../fields/field'; -import { esFilters, IFieldType, IndexPattern } from '../../../../../../../src/plugins/data/public'; -import { PhraseFilter } from '../../../../../../../src/plugins/data/public'; +import { esFilters, IFieldType, IndexPattern } from '../../../../../../src/plugins/data/public'; +import { PhraseFilter } from '../../../../../../src/plugins/data/public'; export class ESTooltipProperty implements ITooltipProperty { private readonly _tooltipProperty: ITooltipProperty; diff --git a/x-pack/legacy/plugins/maps/public/layers/tooltips/join_tooltip_property.ts b/x-pack/plugins/maps/public/layers/tooltips/join_tooltip_property.ts similarity index 96% rename from x-pack/legacy/plugins/maps/public/layers/tooltips/join_tooltip_property.ts rename to x-pack/plugins/maps/public/layers/tooltips/join_tooltip_property.ts index 02f0920ce3c619..4af236f6e9e364 100644 --- a/x-pack/legacy/plugins/maps/public/layers/tooltips/join_tooltip_property.ts +++ b/x-pack/plugins/maps/public/layers/tooltips/join_tooltip_property.ts @@ -6,7 +6,7 @@ import { ITooltipProperty } from './tooltip_property'; import { IJoin } from '../joins/join'; -import { PhraseFilter } from '../../../../../../../src/plugins/data/public'; +import { PhraseFilter } from '../../../../../../src/plugins/data/public'; export class JoinTooltipProperty implements ITooltipProperty { private readonly _tooltipProperty: ITooltipProperty; diff --git a/x-pack/legacy/plugins/maps/public/layers/tooltips/tooltip_property.ts b/x-pack/plugins/maps/public/layers/tooltips/tooltip_property.ts similarity index 92% rename from x-pack/legacy/plugins/maps/public/layers/tooltips/tooltip_property.ts rename to x-pack/plugins/maps/public/layers/tooltips/tooltip_property.ts index 46e27bbd770a17..7d680dfe9cae00 100644 --- a/x-pack/legacy/plugins/maps/public/layers/tooltips/tooltip_property.ts +++ b/x-pack/plugins/maps/public/layers/tooltips/tooltip_property.ts @@ -5,8 +5,8 @@ */ import _ from 'lodash'; -import { PhraseFilter } from '../../../../../../../src/plugins/data/public'; -import { TooltipFeature } from '../../../../../../plugins/maps/common/descriptor_types'; +import { PhraseFilter } from '../../../../../../src/plugins/data/public'; +import { TooltipFeature } from '../../../../../plugins/maps/common/descriptor_types'; export interface ITooltipProperty { getPropertyKey(): string; diff --git a/x-pack/legacy/plugins/maps/public/layers/util/assign_feature_ids.test.ts b/x-pack/plugins/maps/public/layers/util/assign_feature_ids.test.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/util/assign_feature_ids.test.ts rename to x-pack/plugins/maps/public/layers/util/assign_feature_ids.test.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/util/assign_feature_ids.ts b/x-pack/plugins/maps/public/layers/util/assign_feature_ids.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/util/assign_feature_ids.ts rename to x-pack/plugins/maps/public/layers/util/assign_feature_ids.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/util/can_skip_fetch.test.js b/x-pack/plugins/maps/public/layers/util/can_skip_fetch.test.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/util/can_skip_fetch.test.js rename to x-pack/plugins/maps/public/layers/util/can_skip_fetch.test.js diff --git a/x-pack/legacy/plugins/maps/public/layers/util/can_skip_fetch.ts b/x-pack/plugins/maps/public/layers/util/can_skip_fetch.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/util/can_skip_fetch.ts rename to x-pack/plugins/maps/public/layers/util/can_skip_fetch.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/util/data_request.ts b/x-pack/plugins/maps/public/layers/util/data_request.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/util/data_request.ts rename to x-pack/plugins/maps/public/layers/util/data_request.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/util/es_agg_utils.test.ts b/x-pack/plugins/maps/public/layers/util/es_agg_utils.test.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/util/es_agg_utils.test.ts rename to x-pack/plugins/maps/public/layers/util/es_agg_utils.test.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/util/es_agg_utils.ts b/x-pack/plugins/maps/public/layers/util/es_agg_utils.ts similarity index 95% rename from x-pack/legacy/plugins/maps/public/layers/util/es_agg_utils.ts rename to x-pack/plugins/maps/public/layers/util/es_agg_utils.ts index 9d4f24f80d6cd1..329a2a6fc64fb1 100644 --- a/x-pack/legacy/plugins/maps/public/layers/util/es_agg_utils.ts +++ b/x-pack/plugins/maps/public/layers/util/es_agg_utils.ts @@ -5,7 +5,7 @@ */ import { i18n } from '@kbn/i18n'; import _ from 'lodash'; -import { IndexPattern, IFieldType } from '../../../../../../../src/plugins/data/public'; +import { IndexPattern, IFieldType } from '../../../../../../src/plugins/data/public'; import { TOP_TERM_PERCENTAGE_SUFFIX } from '../../../common/constants'; export function getField(indexPattern: IndexPattern, fieldName: string) { diff --git a/x-pack/legacy/plugins/maps/public/layers/util/is_metric_countable.ts b/x-pack/plugins/maps/public/layers/util/is_metric_countable.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/util/is_metric_countable.ts rename to x-pack/plugins/maps/public/layers/util/is_metric_countable.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/util/is_refresh_only_query.ts b/x-pack/plugins/maps/public/layers/util/is_refresh_only_query.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/util/is_refresh_only_query.ts rename to x-pack/plugins/maps/public/layers/util/is_refresh_only_query.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/util/mb_filter_expressions.ts b/x-pack/plugins/maps/public/layers/util/mb_filter_expressions.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/util/mb_filter_expressions.ts rename to x-pack/plugins/maps/public/layers/util/mb_filter_expressions.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/vector_layer.d.ts b/x-pack/plugins/maps/public/layers/vector_layer.d.ts similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/vector_layer.d.ts rename to x-pack/plugins/maps/public/layers/vector_layer.d.ts diff --git a/x-pack/legacy/plugins/maps/public/layers/vector_layer.js b/x-pack/plugins/maps/public/layers/vector_layer.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/vector_layer.js rename to x-pack/plugins/maps/public/layers/vector_layer.js diff --git a/x-pack/legacy/plugins/maps/public/layers/vector_tile_layer.js b/x-pack/plugins/maps/public/layers/vector_tile_layer.js similarity index 100% rename from x-pack/legacy/plugins/maps/public/layers/vector_tile_layer.js rename to x-pack/plugins/maps/public/layers/vector_tile_layer.js diff --git a/x-pack/legacy/plugins/maps/public/meta.js b/x-pack/plugins/maps/public/meta.js similarity index 76% rename from x-pack/legacy/plugins/maps/public/meta.js rename to x-pack/plugins/maps/public/meta.js index 4d81785ff7a0ab..d4612554cf00b9 100644 --- a/x-pack/legacy/plugins/maps/public/meta.js +++ b/x-pack/plugins/maps/public/meta.js @@ -11,20 +11,19 @@ import { EMS_GLYPHS_PATH, EMS_APP_NAME, } from '../common/constants'; -import chrome from 'ui/chrome'; import { i18n } from '@kbn/i18n'; import { EMSClient } from '@elastic/ems-client'; -import { getLicenseId } from './kibana_services'; +import { getInjectedVarFunc, getLicenseId } from './kibana_services'; import fetch from 'node-fetch'; const GIS_API_RELATIVE = `../${GIS_API_PATH}`; export function getKibanaRegionList() { - return chrome.getInjected('regionmapLayers'); + return getInjectedVarFunc()('regionmapLayers'); } export function getKibanaTileMap() { - return chrome.getInjected('tilemap'); + return getInjectedVarFunc()('tilemap'); } function relativeToAbsolute(url) { @@ -41,27 +40,27 @@ let emsClient = null; let latestLicenseId = null; export function getEMSClient() { if (!emsClient) { - const isEmsEnabled = chrome.getInjected('isEmsEnabled', true); + const isEmsEnabled = getInjectedVarFunc()('isEmsEnabled', true); if (isEmsEnabled) { - const proxyElasticMapsServiceInMaps = chrome.getInjected( + const proxyElasticMapsServiceInMaps = getInjectedVarFunc()( 'proxyElasticMapsServiceInMaps', false ); const proxyPath = ''; const tileApiUrl = proxyElasticMapsServiceInMaps ? relativeToAbsolute(`${GIS_API_RELATIVE}/${EMS_TILES_CATALOGUE_PATH}`) - : chrome.getInjected('emsTileApiUrl'); + : getInjectedVarFunc()('emsTileApiUrl'); const fileApiUrl = proxyElasticMapsServiceInMaps ? relativeToAbsolute(`${GIS_API_RELATIVE}/${EMS_FILES_CATALOGUE_PATH}`) - : chrome.getInjected('emsFileApiUrl'); + : getInjectedVarFunc()('emsFileApiUrl'); emsClient = new EMSClient({ language: i18n.getLocale(), - appVersion: chrome.getInjected('kbnPkgVersion'), + appVersion: getInjectedVarFunc()('kbnPkgVersion'), appName: EMS_APP_NAME, tileApiUrl, fileApiUrl, - landingPageUrl: chrome.getInjected('emsLandingPageUrl'), + landingPageUrl: getInjectedVarFunc()('emsLandingPageUrl'), fetchFunction: fetchFunction, //import this from client-side, so the right instance is returned (bootstrapped from common/* would not work proxyPath, }); @@ -87,13 +86,13 @@ export function getEMSClient() { } export function getGlyphUrl() { - if (!chrome.getInjected('isEmsEnabled', true)) { + if (!getInjectedVarFunc()('isEmsEnabled', true)) { return ''; } - return chrome.getInjected('proxyElasticMapsServiceInMaps', false) + return getInjectedVarFunc()('proxyElasticMapsServiceInMaps', false) ? relativeToAbsolute(`../${GIS_API_PATH}/${EMS_TILES_CATALOGUE_PATH}/${EMS_GLYPHS_PATH}`) + `/{fontstack}/{range}` - : chrome.getInjected('emsFontLibraryUrl', true); + : getInjectedVarFunc()('emsFontLibraryUrl', true); } export function isRetina() { diff --git a/x-pack/legacy/plugins/maps/public/meta.test.js b/x-pack/plugins/maps/public/meta.test.js similarity index 57% rename from x-pack/legacy/plugins/maps/public/meta.test.js rename to x-pack/plugins/maps/public/meta.test.js index 64dd73fe109ff2..d83f2adb35ef7f 100644 --- a/x-pack/legacy/plugins/maps/public/meta.test.js +++ b/x-pack/plugins/maps/public/meta.test.js @@ -9,39 +9,24 @@ import { getEMSClient } from './meta'; jest.mock('@elastic/ems-client'); -jest.mock('ui/chrome', () => ({ - getBasePath: () => { - return ''; - }, - getInjected(key) { - if (key === 'proxyElasticMapsServiceInMaps') { - return false; - } else if (key === 'isEmsEnabled') { - return true; - } else if (key === 'emsFileApiUrl') { - return 'https://file-api'; - } else if (key === 'emsTileApiUrl') { - return 'https://tile-api'; - } - }, - getUiSettingsClient: () => { - return { - get: () => { - return ''; - }, +describe('default use without proxy', () => { + beforeEach(() => { + require('./kibana_services').getInjectedVarFunc = () => key => { + if (key === 'proxyElasticMapsServiceInMaps') { + return false; + } else if (key === 'isEmsEnabled') { + return true; + } else if (key === 'emsFileApiUrl') { + return 'https://file-api'; + } else if (key === 'emsTileApiUrl') { + return 'https://tile-api'; + } }; - }, -})); - -jest.mock('./kibana_services', () => { - return { - getLicenseId() { + require('./kibana_services').getLicenseId = () => { return 'foobarlicenseid'; - }, - }; -}); + }; + }); -describe('default use without proxy', () => { it('should construct EMSClient with absolute file and tile API urls', async () => { getEMSClient(); const mockEmsClientCall = EMSClient.mock.calls[0]; diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index 506b0c426f0fae..9437c2512ded49 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -8,6 +8,20 @@ import { Plugin, CoreSetup, CoreStart } from 'src/core/public'; import { Setup as InspectorSetupContract } from 'src/plugins/inspector/public'; // @ts-ignore import { MapView } from './inspector/views/map_view'; +import { + setAutocompleteService, + setFileUpload, + setHttp, + setIndexPatternSelect, + setIndexPatternService, + setInjectedVarFunc, + setInspector, + setLicenseId, + setTimeFilter, + setToasts, + setUiSettings, + // @ts-ignore +} from './kibana_services'; export interface MapsPluginSetupDependencies { inspector: InspectorSetupContract; @@ -15,6 +29,29 @@ export interface MapsPluginSetupDependencies { // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface MapsPluginStartDependencies {} +export const bindSetupCoreAndPlugins = (core: CoreSetup, plugins: any) => { + const { licensing } = plugins; + const { injectedMetadata, http } = core; + if (licensing) { + licensing.license$.subscribe(({ uid }: { uid: string }) => setLicenseId(uid)); + } + setInjectedVarFunc(injectedMetadata.getInjectedVar); + setHttp(http); + setUiSettings(core.uiSettings); + setInjectedVarFunc(core.injectedMetadata.getInjectedVar); + setToasts(core.notifications.toasts); +}; + +export const bindStartCoreAndPlugins = (core: CoreStart, plugins: any) => { + const { file_upload, data, inspector } = plugins; + setInspector(inspector); + setFileUpload(file_upload); + setIndexPatternSelect(data.ui.IndexPatternSelect); + setTimeFilter(data.query.timefilter.timefilter); + setIndexPatternService(data.indexPatterns); + setAutocompleteService(data.autocomplete); +}; + /** * These are the interfaces with your public contracts. You should export these * for other plugins to use in _their_ `SetupDeps`/`StartDeps` interfaces. diff --git a/x-pack/plugins/ml/common/constants/file_datavisualizer.ts b/x-pack/plugins/ml/common/constants/file_datavisualizer.ts index d72e4d63cc47e8..81d51bfa258160 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/errors.ts b/x-pack/plugins/ml/common/types/errors.ts index 63e222490082bb..284250bd5ce55b 100644 --- a/x-pack/plugins/ml/common/types/errors.ts +++ b/x-pack/plugins/ml/common/types/errors.ts @@ -9,6 +9,7 @@ export interface ErrorResponse { statusCode: number; error: string; message: string; + attributes?: any; }; name: string; } diff --git a/x-pack/plugins/ml/common/types/file_datavisualizer.ts b/x-pack/plugins/ml/common/types/file_datavisualizer.ts index bc03f82673a1fe..f771547b978114 100644 --- a/x-pack/plugins/ml/common/types/file_datavisualizer.ts +++ b/x-pack/plugins/ml/common/types/file_datavisualizer.ts @@ -4,6 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ +export interface InputOverrides { + [key: string]: string; +} + +export type FormattedOverrides = InputOverrides & { + column_names: string[]; + has_header_row: boolean; + should_trim_fields: boolean; +}; + +export interface AnalysisResult { + results: FindFileStructureResponse; + overrides?: FormattedOverrides; +} + export interface FindFileStructureResponse { charset: string; has_header_row: boolean; @@ -28,4 +43,54 @@ export interface FindFileStructureResponse { need_client_timezone: boolean; num_lines_analyzed: number; column_names: string[]; + explanation?: string[]; + grok_pattern?: string; + multiline_start_pattern?: string; + exclude_lines_pattern?: string; + java_timestamp_formats?: string[]; + joda_timestamp_formats?: string[]; + timestamp_field?: string; + should_trim_fields?: boolean; +} + +export interface ImportResponse { + success: boolean; + id: string; + index?: string; + pipelineId?: string; + docCount: number; + failures: ImportFailure[]; + error?: any; + ingestError?: boolean; +} + +export interface ImportFailure { + item: number; + reason: string; + doc: Doc; +} + +export interface Doc { + message: string; +} + +export interface Settings { + pipeline?: string; + index: string; + body: any[]; + [key: string]: any; +} + +export interface Mappings { + [key: string]: any; +} + +export interface IngestPipelineWrapper { + id: string; + pipeline: IngestPipeline; +} + +export interface IngestPipeline { + description: string; + processors: any[]; } 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 00000000000000..8fd9fd22bad8af --- /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 e9796fcbb0fe44..f1facd18b9da5b 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_exploration/components/classification_exploration/results_table.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/results_table.tsx index fbdb47c87c7efa..9758dd969b443f 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/results_table.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/results_table.tsx @@ -211,6 +211,7 @@ export const ResultsTable: FC = React.memo( switch (type) { case ES_FIELD_TYPES.BOOLEAN: column.dataType = ES_FIELD_TYPES.BOOLEAN; + column.render = d => (d ? 'true' : 'false'); break; case ES_FIELD_TYPES.DATE: column.align = 'right'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/results_table.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/results_table.tsx index 8d53a9278a1afd..a35be5400f46bc 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/results_table.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/results_table.tsx @@ -213,6 +213,7 @@ export const ResultsTable: FC = React.memo( switch (type) { case ES_FIELD_TYPES.BOOLEAN: column.dataType = ES_FIELD_TYPES.BOOLEAN; + column.render = d => (d ? 'true' : 'false'); break; case ES_FIELD_TYPES.DATE: column.align = 'right'; 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 e5f30a50ed8f0d..0c83dfb6a2346e 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 9c0bc69f4b41f9..4bd03fec7cc729 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 01a39d2ef9f3b6..e121268e65e86c 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 a8bb5a0a8fe102..2d6505f5ce1f75 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/about_panel.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/about_panel.tsx similarity index 91% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/about_panel.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/about_panel.tsx index edecc925591d37..2bddf0de0499da 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/about_panel.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/about_panel.tsx @@ -6,7 +6,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import React from 'react'; +import React, { FC } from 'react'; import { EuiFlexGroup, @@ -23,7 +23,11 @@ import { import { WelcomeContent } from './welcome_content'; -export function AboutPanel({ onFilePickerChange }) { +interface Props { + onFilePickerChange(files: FileList | null): void; +} + +export const AboutPanel: FC = ({ onFilePickerChange }) => { return ( @@ -54,9 +58,9 @@ export function AboutPanel({ onFilePickerChange }) { ); -} +}; -export function LoadingPanel() { +export const LoadingPanel: FC = () => { return ( @@ -79,4 +83,4 @@ export function LoadingPanel() { ); -} +}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/index.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/index.ts similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/index.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/index.ts diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/welcome_content.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/welcome_content.tsx similarity index 88% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/welcome_content.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/welcome_content.tsx index 73d12122879f8c..49b2396aeca13e 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/welcome_content.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/about_panel/welcome_content.tsx @@ -5,7 +5,8 @@ */ import { FormattedMessage } from '@kbn/i18n/react'; -import React from 'react'; +import React, { FC } from 'react'; +import { i18n } from '@kbn/i18n'; import { EuiFlexGroup, @@ -18,8 +19,18 @@ import { } from '@elastic/eui'; import { ExperimentalBadge } from '../experimental_badge'; +import { getMaxBytesFormatted } from '../utils'; + +export const WelcomeContent: FC = () => { + const toolTipContent = i18n.translate( + 'xpack.ml.fileDatavisualizer.welcomeContent.experimentalFeatureTooltip', + { + defaultMessage: "Experimental feature. We'd love to hear your feedback.", + } + ); + + const maxFileSize = getMaxBytesFormatted(); -export function WelcomeContent() { return ( @@ -32,16 +43,7 @@ export function WelcomeContent() { id="xpack.ml.fileDatavisualizer.welcomeContent.visualizeDataFromLogFileTitle" defaultMessage="Visualize data from a log file {experimentalBadge}" values={{ - experimentalBadge: ( - - } - /> - ), + experimentalBadge: , }} /> @@ -118,7 +120,8 @@ export function WelcomeContent() {

@@ -144,4 +147,4 @@ export function WelcomeContent() {
); -} +}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/analysis_summary/analysis_summary.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/analysis_summary/analysis_summary.tsx similarity index 89% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/analysis_summary/analysis_summary.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/analysis_summary/analysis_summary.tsx index b3bf4ea4daf83f..b80db3b27fa7e5 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/analysis_summary/analysis_summary.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/analysis_summary/analysis_summary.tsx @@ -5,11 +5,12 @@ */ import { FormattedMessage } from '@kbn/i18n/react'; -import React from 'react'; +import React, { FC } from 'react'; import { EuiTitle, EuiSpacer, EuiDescriptionList } from '@elastic/eui'; +import { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer'; -export function AnalysisSummary({ results }) { +export const AnalysisSummary: FC<{ results: FindFileStructureResponse }> = ({ results }) => { const items = createDisplayItems(results); return ( @@ -28,10 +29,10 @@ export function AnalysisSummary({ results }) { ); -} +}; -function createDisplayItems(results) { - const items = [ +function createDisplayItems(results: FindFileStructureResponse) { + const items: Array<{ title: any; description: string | number }> = [ { title: ( = ({ tooltipContent }) => { return ( ); -} +}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/experimental_badge/index.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/experimental_badge/index.ts similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/experimental_badge/index.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/experimental_badge/index.ts diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/explanation_flyout/explanation_flyout.tsx b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/explanation_flyout/explanation_flyout.tsx new file mode 100644 index 00000000000000..8e44a296126b61 --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/explanation_flyout/explanation_flyout.tsx @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FC } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiFlyout, + EuiFlyoutFooter, + EuiFlexGroup, + EuiFlexItem, + EuiFlyoutHeader, + EuiButtonEmpty, + EuiTitle, + EuiFlyoutBody, + EuiSpacer, + EuiText, + EuiSubSteps, +} from '@elastic/eui'; +import { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer'; + +interface Props { + results: FindFileStructureResponse; + closeFlyout(): void; +} +export const ExplanationFlyout: FC = ({ results, closeFlyout }) => { + const explanation = results.explanation!; + return ( + + + +

+ +

+
+
+ + + + + + + + + + + + +
+ ); +}; + +const Content: FC<{ explanation: string[] }> = ({ explanation }) => ( + <> + + + + + +
    + {explanation.map((e, i) => ( +
  • + {e} + +
  • + ))} +
+
+
+ +); diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/explanation_flyout/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/explanation_flyout/index.ts new file mode 100644 index 00000000000000..e2880504038878 --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/explanation_flyout/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { ExplanationFlyout } from './explanation_flyout'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_contents/file_contents.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_contents/file_contents.tsx similarity index 83% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_contents/file_contents.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_contents/file_contents.tsx index ed5ab57a2588d0..6564b9a1f4d838 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_contents/file_contents.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_contents/file_contents.tsx @@ -5,13 +5,19 @@ */ import { FormattedMessage } from '@kbn/i18n/react'; -import React from 'react'; +import React, { FC } from 'react'; import { EuiTitle, EuiSpacer } from '@elastic/eui'; import { MLJobEditor, ML_EDITOR_MODE } from '../../../../jobs/jobs_list/components/ml_job_editor'; -export function FileContents({ data, format, numberOfLines }) { +interface Props { + data: string; + format: string; + numberOfLines: number; +} + +export const FileContents: FC = ({ data, format, numberOfLines }) => { let mode = ML_EDITOR_MODE.TEXT; if (format === ML_EDITOR_MODE.JSON) { mode = ML_EDITOR_MODE.JSON; @@ -35,7 +41,7 @@ export function FileContents({ data, format, numberOfLines }) { id="xpack.ml.fileDatavisualizer.fileContents.firstLinesDescription" defaultMessage="First {numberOfLines, plural, zero {# line} one {# line} other {# lines}}" values={{ - numberOfLines: numberOfLines, + numberOfLines, }} /> @@ -51,9 +57,9 @@ export function FileContents({ data, format, numberOfLines }) { /> ); -} +}; -function limitByNumberOfLines(data, numberOfLines) { +function limitByNumberOfLines(data: string, numberOfLines: number) { return data .split('\n') .slice(0, numberOfLines) diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_contents/index.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_contents/index.ts similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_contents/index.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/file_contents/index.ts 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 12e5a14b518713..d1b615a878b2b5 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 @@ -17,16 +17,17 @@ import { BottomBar } from '../bottom_bar'; import { ResultsView } from '../results_view'; 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 { isErrorResponse } from '../../../../../../common/types/errors'; import { + getMaxBytes, readFile, createUrlOverrides, processResults, reduceData, hasImportPermission, } from '../utils'; + import { MODE } from './constants'; const UPLOAD_SIZE_MB = 5; @@ -42,12 +43,14 @@ export class FileDataVisualizerView extends Component { fileSize: 0, fileTooLarge: false, fileCouldNotBeRead: false, - serverErrorMessage: '', + serverError: null, loading: false, loaded: false, results: undefined, + explanation: undefined, mode: MODE.READ, isEditFlyoutVisible: false, + isExplanationFlyoutVisible: false, bottomBarVisible: false, hasPermissionToImport: false, }; @@ -55,6 +58,7 @@ export class FileDataVisualizerView extends Component { this.overrides = {}; this.previousOverrides = {}; this.originalSettings = {}; + this.maxFileUploadBytes = getMaxBytes(); } async componentDidMount() { @@ -78,8 +82,9 @@ export class FileDataVisualizerView extends Component { fileSize: 0, fileTooLarge: false, fileCouldNotBeRead: false, - serverErrorMessage: '', + serverError: null, results: undefined, + explanation: undefined, }, () => { if (files.length) { @@ -90,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; @@ -102,7 +107,6 @@ export class FileDataVisualizerView extends Component { await this.loadSettings(data); } catch (error) { - console.error(error); this.setState({ loaded: false, loading: false, @@ -128,7 +132,7 @@ export class FileDataVisualizerView extends Component { console.log('overrides', overrides); const { analyzeFile } = ml.fileDatavisualizer; const resp = await analyzeFile(lessData, overrides); - const serverSettings = processResults(resp.results); + const serverSettings = processResults(resp); const serverOverrides = resp.overrides; this.previousOverrides = this.overrides; @@ -172,26 +176,19 @@ export class FileDataVisualizerView extends Component { this.setState({ results: resp.results, + explanation: resp.explanation, loaded: true, loading: false, fileCouldNotBeRead: isRetry, }); } catch (error) { - console.error(error); - - let serverErrorMsg; - if (isErrorResponse(error) === true) { - serverErrorMsg = `${error.body.error}: ${error.body.message}`; - } else { - serverErrorMsg = JSON.stringify(error, null, 2); - } - this.setState({ results: undefined, + explanation: undefined, loaded: false, loading: false, fileCouldNotBeRead: true, - serverErrorMessage: serverErrorMsg, + serverError: error, }); // as long as the previous overrides are different to the current overrides, @@ -216,6 +213,16 @@ export class FileDataVisualizerView extends Component { this.hideBottomBar(); }; + closeExplanationFlyout = () => { + this.setState({ isExplanationFlyoutVisible: false }); + this.showBottomBar(); + }; + + showExplanationFlyout = () => { + this.setState({ isExplanationFlyoutVisible: true }); + this.hideBottomBar(); + }; + showBottomBar = () => { this.setState({ bottomBarVisible: true }); }; @@ -252,14 +259,16 @@ export class FileDataVisualizerView extends Component { loading, loaded, results, + explanation, fileContents, fileName, fileSize, fileTooLarge, fileCouldNotBeRead, - serverErrorMessage, + serverError, mode, isEditFlyoutVisible, + isExplanationFlyoutVisible, bottomBarVisible, hasPermissionToImport, } = this.state; @@ -277,11 +286,13 @@ export class FileDataVisualizerView extends Component { {loading && } - {fileTooLarge && } + {fileTooLarge && ( + + )} {fileCouldNotBeRead && loading === false && ( - + )} @@ -289,9 +300,12 @@ export class FileDataVisualizerView extends Component { {loaded && ( this.showEditFlyout()} + showExplanationFlyout={() => this.showExplanationFlyout()} + disableButtons={isEditFlyoutVisible || isExplanationFlyoutVisible} /> )} + {isExplanationFlyoutVisible && ( + + )} + {bottomBarVisible && loaded && ( = ({ fileSize, maxFileSize }) => { const fileSizeFormatted = numeral(fileSize).format(FILE_SIZE_DISPLAY_FORMAT); const maxFileSizeFormatted = numeral(maxFileSize).format(FILE_SIZE_DISPLAY_FORMAT); @@ -67,9 +72,15 @@ export function FileTooLarge({ fileSize, maxFileSize }) { {errorText}
); +}; + +interface FileCouldNotBeReadProps { + error: ErrorResponse; + loaded: boolean; } -export function FileCouldNotBeRead({ error, loaded }) { +export const FileCouldNotBeRead: FC = ({ error, loaded }) => { + const message = error?.body?.message || ''; return ( - {error !== undefined &&

{error}

} + {message} + {loaded && ( -

+ <> + -

+ )}
); -} +}; + +export const Explanation: FC<{ error: ErrorResponse }> = ({ error }) => { + if (!error?.body?.attributes?.body?.error?.suppressed?.length) { + return null; + } + const reason: string = error.body.attributes.body.error.suppressed[0].reason; + return ( + <> + + {reason.split('\n').map((m, i) => ( +
{m}
+ ))} + + ); +}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_errors/errors.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_errors/errors.tsx similarity index 85% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_errors/errors.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_errors/errors.tsx index 6629c0109feafd..f723ad1a752bf4 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_errors/errors.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_errors/errors.tsx @@ -5,13 +5,24 @@ */ import { FormattedMessage } from '@kbn/i18n/react'; -import React from 'react'; +import React, { FC } from 'react'; +import { i18n } from '@kbn/i18n'; import { EuiCallOut, EuiAccordion } from '@elastic/eui'; -import { IMPORT_STATUS } from '../import_progress'; +import { IMPORT_STATUS, Statuses } from '../import_progress'; -export function ImportErrors({ errors, statuses }) { +interface ImportError { + msg: string; + more?: string; +} + +interface Props { + errors: any[]; + statuses: Statuses; +} + +export const ImportErrors: FC = ({ errors, statuses }) => { return ( {errors.map((e, i) => ( @@ -19,9 +30,9 @@ export function ImportErrors({ errors, statuses }) { ))} ); -} +}; -function title(statuses) { +function title(statuses: Statuses) { switch (IMPORT_STATUS.FAILED) { case statuses.readStatus: return ( @@ -82,7 +93,7 @@ function title(statuses) { } } -function ImportError(error, key) { +function ImportError(error: any, key: number) { const errorObj = toString(error); return ( @@ -106,7 +117,7 @@ function ImportError(error, key) { ); } -function toString(error) { +function toString(error: any): ImportError { if (typeof error === 'string') { return { msg: error }; } @@ -118,7 +129,7 @@ function toString(error) { if (typeof error.error === 'object') { if (error.error.msg !== undefined) { // this will catch a bulk ingest failure - const errorObj = { msg: error.error.msg }; + const errorObj: ImportError = { msg: error.error.msg }; if (error.error.body !== undefined) { errorObj.more = error.error.response; } @@ -139,11 +150,8 @@ function toString(error) { } return { - msg: ( - - ), + msg: i18n.translate('xpack.ml.fileDatavisualizer.importErrors.unknownErrorMessage', { + defaultMessage: 'Unknown error', + }), }; } diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_errors/index.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_errors/index.ts similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_errors/index.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_errors/index.ts diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_progress/import_progress.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_progress/import_progress.tsx similarity index 92% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_progress/import_progress.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_progress/import_progress.tsx index 272ec2979ad2f0..533fec4ac50c8c 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_progress/import_progress.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_progress/import_progress.tsx @@ -6,17 +6,31 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import React from 'react'; +import React, { FC } from 'react'; import { EuiStepsHorizontal, EuiProgress, EuiSpacer } from '@elastic/eui'; -export const IMPORT_STATUS = { - INCOMPLETE: 'incomplete', - COMPLETE: 'complete', - FAILED: 'danger', -}; +export enum IMPORT_STATUS { + INCOMPLETE = 'incomplete', + COMPLETE = 'complete', + FAILED = 'danger', +} + +export interface Statuses { + reading: boolean; + readStatus: IMPORT_STATUS; + parseJSONStatus: IMPORT_STATUS; + indexCreatedStatus: IMPORT_STATUS; + ingestPipelineCreatedStatus: IMPORT_STATUS; + indexPatternCreatedStatus: IMPORT_STATUS; + uploadProgress: number; + uploadStatus: IMPORT_STATUS; + createIndexPattern: boolean; + createPipeline: boolean; + permissionCheckStatus: IMPORT_STATUS; +} -export function ImportProgress({ statuses }) { +export const ImportProgress: FC<{ statuses: Statuses }> = ({ statuses }) => { const { reading, readStatus, @@ -271,9 +285,9 @@ export function ImportProgress({ statuses }) { )} ); -} +}; -function UploadFunctionProgress({ progress }) { +const UploadFunctionProgress: FC<{ progress: number }> = ({ progress }) => { return (

@@ -290,4 +304,4 @@ function UploadFunctionProgress({ progress }) { )} ); -} +}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_progress/index.ts b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_progress/index.ts new file mode 100644 index 00000000000000..9b0c6ca2264cbd --- /dev/null +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_progress/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { ImportProgress, IMPORT_STATUS, Statuses } from './import_progress'; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/advanced.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/advanced.tsx similarity index 85% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/advanced.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/advanced.tsx index 14cbe67662ed6f..a79a7d36f32945 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/advanced.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/advanced.tsx @@ -6,7 +6,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import React from 'react'; +import React, { FC } from 'react'; import { EuiFieldText, @@ -20,7 +20,25 @@ import { import { MLJobEditor, ML_EDITOR_MODE } from '../../../../jobs/jobs_list/components/ml_job_editor'; const EDITOR_HEIGHT = '300px'; -export function AdvancedSettings({ +interface Props { + index: string; + indexPattern: string; + initialized: boolean; + onIndexChange(): void; + createIndexPattern: boolean; + onCreateIndexPatternChange(): void; + onIndexPatternChange(): void; + indexSettingsString: string; + mappingsString: string; + pipelineString: string; + onIndexSettingsStringChange(): void; + onMappingsStringChange(): void; + onPipelineStringChange(): void; + indexNameError: string; + indexPatternNameError: string; +} + +export const AdvancedSettings: FC = ({ index, indexPattern, initialized, @@ -36,7 +54,7 @@ export function AdvancedSettings({ onPipelineStringChange, indexNameError, indexPatternNameError, -}) { +}) => { return ( } - disabled={createIndexPattern === false || initialized === true} isInvalid={indexPatternNameError !== ''} error={[indexPatternNameError]} > @@ -133,9 +150,15 @@ export function AdvancedSettings({ ); +}; + +interface JsonEditorProps { + initialized: boolean; + data: string; + onChange(): void; } -function IndexSettings({ initialized, data, onChange }) { +const IndexSettings: FC = ({ initialized, data, onChange }) => { return ( } - disabled={initialized === true} fullWidth > ); -} +}; -function Mappings({ initialized, data, onChange }) { +const Mappings: FC = ({ initialized, data, onChange }) => { return ( } - disabled={initialized === true} fullWidth > ); -} +}; -function IngestPipeline({ initialized, data, onChange }) { +const IngestPipeline: FC = ({ initialized, data, onChange }) => { return ( } - disabled={initialized === true} fullWidth > ); -} +}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/import_settings.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/import_settings.tsx similarity index 82% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/import_settings.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/import_settings.tsx index ba637c472333d3..02cf647caaf16d 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/import_settings.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/import_settings.tsx @@ -5,14 +5,32 @@ */ import { i18n } from '@kbn/i18n'; -import React from 'react'; +import React, { FC } from 'react'; import { EuiTabbedContent, EuiSpacer } from '@elastic/eui'; import { SimpleSettings } from './simple'; import { AdvancedSettings } from './advanced'; -export const ImportSettings = ({ +interface Props { + index: string; + indexPattern: string; + initialized: boolean; + onIndexChange(): void; + createIndexPattern: boolean; + onCreateIndexPatternChange(): void; + onIndexPatternChange(): void; + indexSettingsString: string; + mappingsString: string; + pipelineString: string; + onIndexSettingsStringChange(): void; + onMappingsStringChange(): void; + onPipelineStringChange(): void; + indexNameError: string; + indexPatternNameError: string; +} + +export const ImportSettings: FC = ({ index, indexPattern, initialized, diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/index.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/index.ts similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/index.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/index.ts diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/simple.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/simple.tsx similarity index 88% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/simple.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/simple.tsx index 271b9493aa1f35..1e716824729e33 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/simple.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_settings/simple.tsx @@ -6,11 +6,20 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import React from 'react'; +import React, { FC } from 'react'; import { EuiFieldText, EuiFormRow, EuiCheckbox, EuiSpacer } from '@elastic/eui'; -export const SimpleSettings = ({ +interface Props { + index: string; + initialized: boolean; + onIndexChange(): void; + createIndexPattern: boolean; + onCreateIndexPatternChange(): void; + indexNameError: string; +} + +export const SimpleSettings: FC = ({ index, initialized, onIndexChange, diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_summary/import_summary.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_summary/import_summary.tsx similarity index 86% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_summary/import_summary.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_summary/import_summary.tsx index 0e67807a39fd92..6ee17d401bd707 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_summary/import_summary.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_summary/import_summary.tsx @@ -5,11 +5,29 @@ */ import { FormattedMessage } from '@kbn/i18n/react'; -import React from 'react'; +import React, { FC } from 'react'; import { EuiSpacer, EuiDescriptionList, EuiCallOut, EuiAccordion } from '@elastic/eui'; -export function ImportSummary({ +interface Props { + index: string; + indexPattern: string; + ingestPipelineId: string; + docCount: number; + importFailures: DocFailure[]; + createIndexPattern: boolean; + createPipeline: boolean; +} + +interface DocFailure { + item: number; + reason: string; + doc: { + message: string; + }; +} + +export const ImportSummary: FC = ({ index, indexPattern, ingestPipelineId, @@ -17,7 +35,7 @@ export function ImportSummary({ importFailures, createIndexPattern, createPipeline, -}) { +}) => { const items = createDisplayItems( index, indexPattern, @@ -75,9 +93,13 @@ export function ImportSummary({ )} ); +}; + +interface FailuresProps { + failedDocs: DocFailure[]; } -function Failures({ failedDocs }) { +const Failures: FC = ({ failedDocs }) => { return ( ); -} +}; function createDisplayItems( - index, - indexPattern, - ingestPipelineId, - docCount, - importFailures, - createIndexPattern, - createPipeline + index: string, + indexPattern: string, + ingestPipelineId: string, + docCount: number, + importFailures: DocFailure[], + createIndexPattern: boolean, + createPipeline: boolean ) { const items = [ { diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_summary/index.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_summary/index.ts similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_summary/index.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_summary/index.ts diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js index 0a58153e374dfc..4c9579bfd4b46e 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/import_view.js @@ -623,7 +623,6 @@ async function createKibanaIndexPattern( id, }; } catch (error) { - console.error(error); return { success: false, error, diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/importer.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/importer.ts similarity index 73% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/importer.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/importer.ts index 27899a58beed2f..c97f1c147c4543 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/importer.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/importer.ts @@ -4,30 +4,53 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ml } from '../../../../../services/ml_api_service'; import { chunk } from 'lodash'; import moment from 'moment'; import { i18n } from '@kbn/i18n'; +import { ml } from '../../../../../services/ml_api_service'; +import { + Doc, + ImportFailure, + ImportResponse, + Mappings, + Settings, + IngestPipeline, +} from '../../../../../../../common/types/file_datavisualizer'; const CHUNK_SIZE = 5000; const MAX_CHUNK_CHAR_COUNT = 1000000; const IMPORT_RETRIES = 5; +export interface ImportConfig { + settings: Settings; + mappings: Mappings; + pipeline: IngestPipeline; +} + +export interface ImportResults { + success: boolean; + failures?: any[]; + docCount?: number; + error?: any; +} + export class Importer { - constructor({ settings, mappings, pipeline }) { - this.settings = settings; - this.mappings = mappings; - this.pipeline = pipeline; - - this.data = []; - this.docArray = []; - this.docSizeArray = []; + private _settings: Settings; + private _mappings: Mappings; + private _pipeline: IngestPipeline; + + protected _docArray: Doc[] = []; + + constructor({ settings, mappings, pipeline }: ImportConfig) { + this._settings = settings; + this._mappings = mappings; + this._pipeline = pipeline; } - async initializeImport(index) { - const settings = this.settings; - const mappings = this.mappings; - const pipeline = this.pipeline; + async initializeImport(index: string) { + const settings = this._settings; + const mappings = this._mappings; + const pipeline = this._pipeline; updatePipelineTimezone(pipeline); // if no pipeline has been supplied, @@ -52,7 +75,12 @@ export class Importer { return createIndexResp; } - async import(id, index, pipelineId, setImportProgress) { + async import( + id: string, + index: string, + pipelineId: string, + setImportProgress: (progress: number) => void + ): Promise { if (!id || !index) { return { success: false, @@ -65,14 +93,14 @@ export class Importer { }; } - const chunks = createDocumentChunks(this.docArray); + const chunks = createDocumentChunks(this._docArray); const ingestPipeline = { id: pipelineId, }; let success = true; - const failures = []; + const failures: ImportFailure[] = []; let error; for (let i = 0; i < chunks.length; i++) { @@ -86,10 +114,13 @@ export class Importer { }; let retries = IMPORT_RETRIES; - let resp = { + let resp: ImportResponse = { success: false, failures: [], docCount: 0, + id: '', + index: '', + pipelineId: '', }; while (resp.success === false && retries > 0) { @@ -97,12 +128,14 @@ export class Importer { resp = await ml.fileDatavisualizer.import(aggs); if (retries < IMPORT_RETRIES) { + // eslint-disable-next-line no-console console.log(`Retrying import ${IMPORT_RETRIES - retries}`); } retries--; } catch (err) { - resp = { success: false, error: err }; + resp.success = false; + resp.error = err; retries = 0; } } @@ -110,6 +143,7 @@ export class Importer { if (resp.success) { setImportProgress(((i + 1) / chunks.length) * 100); } else { + // eslint-disable-next-line no-console console.error(resp); success = false; error = resp.error; @@ -120,10 +154,10 @@ export class Importer { populateFailures(resp, failures, i); } - const result = { + const result: ImportResults = { success, failures, - docCount: this.docArray.length, + docCount: this._docArray.length, }; if (success) { @@ -136,7 +170,7 @@ export class Importer { } } -function populateFailures(error, failures, chunkCount) { +function populateFailures(error: ImportResponse, failures: ImportFailure[], chunkCount: number) { if (error.failures && error.failures.length) { // update the item value to include the chunk count // e.g. item 3 in chunk 2 is actually item 20003 @@ -155,10 +189,10 @@ function populateFailures(error, failures, chunkCount) { // But it's not sending every single field that Filebeat would add, so the ingest pipeline // cannot look for a event.timezone variable in each input record. // Therefore we need to replace {{ event.timezone }} with the actual browser timezone -function updatePipelineTimezone(ingestPipeline) { +function updatePipelineTimezone(ingestPipeline: IngestPipeline) { if (ingestPipeline !== undefined && ingestPipeline.processors && ingestPipeline.processors) { const dateProcessor = ingestPipeline.processors.find( - p => p.date !== undefined && p.date.timezone === '{{ event.timezone }}' + (p: any) => p.date !== undefined && p.date.timezone === '{{ event.timezone }}' ); if (dateProcessor) { @@ -167,8 +201,8 @@ function updatePipelineTimezone(ingestPipeline) { } } -function createDocumentChunks(docArray) { - const chunks = []; +function createDocumentChunks(docArray: Doc[]) { + const chunks: Doc[][] = []; // chop docArray into 5000 doc chunks const tempChunks = chunk(docArray, CHUNK_SIZE); diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/importer_factory.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/importer_factory.ts similarity index 76% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/importer_factory.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/importer_factory.ts index 381e8ef6044528..a656b192203682 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/importer_factory.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/importer_factory.ts @@ -6,8 +6,14 @@ import { MessageImporter } from './message_importer'; import { NdjsonImporter } from './ndjson_importer'; +import { ImportConfig } from './importer'; +import { FindFileStructureResponse } from '../../../../../../../common/types/file_datavisualizer'; -export function importerFactory(format, results, settings) { +export function importerFactory( + format: string, + results: FindFileStructureResponse, + settings: ImportConfig +) { switch (format) { // delimited and semi-structured text are both handled by splitting the // file into messages, then sending these to ES for further processing diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/index.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/index.ts similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/index.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/index.ts diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/message_importer.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/message_importer.ts similarity index 76% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/message_importer.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/message_importer.ts index c2d3ac69f0963f..7ccc5a8d673f42 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/message_importer.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/message_importer.ts @@ -4,17 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Importer } from './importer'; +import { Importer, ImportConfig } from './importer'; +import { + Doc, + FindFileStructureResponse, +} from '../../../../../../../common/types/file_datavisualizer'; export class MessageImporter extends Importer { - constructor(results, settings) { + private _excludeLinesRegex: RegExp | null; + private _multilineStartRegex: RegExp | null; + + constructor(results: FindFileStructureResponse, settings: ImportConfig) { super(settings); - this.excludeLinesRegex = + this._excludeLinesRegex = results.exclude_lines_pattern === undefined ? null : new RegExp(results.exclude_lines_pattern); - this.multilineStartRegex = + this._multilineStartRegex = results.multiline_start_pattern === undefined ? null : new RegExp(results.multiline_start_pattern); @@ -26,9 +33,9 @@ export class MessageImporter extends Importer { // multiline_start_pattern regex // if it does, it is a legitimate end of line and can be pushed into the list, // if not, it must be a newline char inside a field value, so keep looking. - read(text) { + read(text: string) { try { - const data = []; + const data: Doc[] = []; let message = ''; let line = ''; @@ -57,14 +64,12 @@ export class MessageImporter extends Importer { data.shift(); } - this.data = data; - this.docArray = this.data; + this._docArray = data; return { success: true, }; } catch (error) { - console.error(error); return { success: false, error, @@ -72,9 +77,9 @@ export class MessageImporter extends Importer { } } - processLine(data, message, line) { - if (this.excludeLinesRegex === null || line.match(this.excludeLinesRegex) === null) { - if (this.multilineStartRegex === null || line.match(this.multilineStartRegex) !== null) { + processLine(data: Doc[], message: string, line: string) { + if (this._excludeLinesRegex === null || line.match(this._excludeLinesRegex) === null) { + if (this._multilineStartRegex === null || line.match(this._multilineStartRegex) !== null) { this.addMessage(data, message); message = ''; } else if (data.length === 0) { @@ -90,7 +95,7 @@ export class MessageImporter extends Importer { return message; } - addMessage(data, message) { + addMessage(data: Doc[], message: string) { // if the message ended \r\n (Windows line endings) // then omit the \r as well as the \n for consistency message = message.replace(/\r$/, ''); diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/ndjson_importer.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/ndjson_importer.ts similarity index 71% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/ndjson_importer.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/ndjson_importer.ts index 887bf1a41200ad..7f5f37abc5246c 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/ndjson_importer.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/import_view/importer/ndjson_importer.ts @@ -4,18 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Importer } from './importer'; +import { Importer, ImportConfig } from './importer'; +import { FindFileStructureResponse } from '../../../../../../../common/types/file_datavisualizer'; export class NdjsonImporter extends Importer { - constructor(results, settings) { + constructor(results: FindFileStructureResponse, settings: ImportConfig) { super(settings); } - read(json) { + read(json: string) { try { const splitJson = json.split(/}\s*\n/); - const ndjson = []; + const ndjson: any[] = []; for (let i = 0; i < splitJson.length; i++) { if (splitJson[i] !== '') { // note the extra } at the end of the line, adding back @@ -24,7 +25,7 @@ export class NdjsonImporter extends Importer { } } - this.docArray = ndjson; + this._docArray = ndjson; return { success: true, diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_view/index.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_view/index.ts similarity index 100% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_view/index.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_view/index.ts diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_view/results_view.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_view/results_view.tsx similarity index 58% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_view/results_view.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_view/results_view.tsx index 96116e5cefa010..f9de03c119d28f 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_view/results_view.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/results_view/results_view.tsx @@ -7,10 +7,10 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import React from 'react'; - +import React, { FC } from 'react'; import { EuiButton, + EuiButtonEmpty, EuiPage, EuiPageBody, EuiPageContentHeader, @@ -18,13 +18,33 @@ import { EuiTabbedContent, EuiSpacer, EuiTitle, + EuiFlexGroup, + EuiFlexItem, } from '@elastic/eui'; +import { FindFileStructureResponse } from '../../../../../../common/types/file_datavisualizer'; import { FileContents } from '../file_contents'; import { AnalysisSummary } from '../analysis_summary'; +// @ts-ignore import { FieldsStats } from '../fields_stats'; -export const ResultsView = ({ data, fileName, results, showEditFlyout }) => { +interface Props { + data: string; + fileName: string; + results: FindFileStructureResponse; + showEditFlyout(): void; + showExplanationFlyout(): void; + disableButtons: boolean; +} + +export const ResultsView: FC = ({ + data, + fileName, + results, + showEditFlyout, + showExplanationFlyout, + disableButtons, +}) => { const tabs = [ { id: 'file-stats', @@ -60,12 +80,24 @@ export const ResultsView = ({ data, fileName, results, showEditFlyout }) => { - showEditFlyout()}> - - + + + showEditFlyout()} disabled={disableButtons}> + + + + + showExplanationFlyout()} disabled={disableButtons}> + + + + diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/index.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/index.ts similarity index 90% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/index.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/index.ts index 6f670359586b7c..0f0036a7c46164 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/index.js +++ 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/overrides.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/overrides.js deleted file mode 100644 index f8a90c87b9dc8d..00000000000000 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/overrides.js +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export const DEFAULT_LINES_TO_SAMPLE = 1000; - -export const overrideDefaults = { - timestampFormat: undefined, - timestampField: undefined, - format: undefined, - delimiter: undefined, - quote: undefined, - hasHeaderRow: undefined, - charset: undefined, - columnNames: undefined, - shouldTrimFields: undefined, - grokPattern: undefined, - linesToSample: undefined, -}; diff --git a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/utils.js b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/utils.ts similarity index 72% rename from x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/utils.js rename to x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/utils.ts index 39cd25ba87d8c1..0d2016b71ed837 100644 --- a/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/utils.js +++ b/x-pack/plugins/ml/public/application/datavisualizer/file_based/components/utils/utils.ts @@ -4,11 +4,33 @@ * you may not use this file except in compliance with the Elastic License. */ -import { overrideDefaults, DEFAULT_LINES_TO_SAMPLE } from './overrides'; import { isEqual } from 'lodash'; +import numeral from '@elastic/numeral'; import { ml } from '../../../../services/ml_api_service'; - -export function readFile(file) { +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; + +const overrideDefaults = { + timestampFormat: undefined, + timestampField: undefined, + format: undefined, + delimiter: undefined, + quote: undefined, + hasHeaderRow: undefined, + charset: undefined, + columnNames: undefined, + shouldTrimFields: undefined, + grokPattern: undefined, + linesToSample: undefined, +}; + +export function readFile(file: File) { return new Promise((resolve, reject) => { if (file && file.size) { const reader = new FileReader(); @@ -23,14 +45,14 @@ export function readFile(file) { resolve({ data }); } }; - })(file); + })(); } else { reject(); } }); } -export function reduceData(data, mb) { +export function reduceData(data: string, mb: number) { // assuming ascii characters in the file where 1 char is 1 byte // TODO - change this when other non UTF-8 formats are // supported for the read data @@ -38,8 +60,17 @@ export function reduceData(data, mb) { return data.length >= size ? data.slice(0, size) : data; } -export function createUrlOverrides(overrides, originalSettings) { - const formattedOverrides = {}; +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) { if (overrideDefaults.hasOwnProperty(o)) { let value = overrides[o]; @@ -93,15 +124,15 @@ export function createUrlOverrides(overrides, originalSettings) { return formattedOverrides; } -export function processResults(results) { +export function processResults({ results, overrides }: AnalysisResult) { const timestampFormat = results.java_timestamp_formats !== undefined && results.java_timestamp_formats.length ? results.java_timestamp_formats[0] : undefined; const linesToSample = - results.overrides !== undefined && results.overrides.lines_to_sample !== undefined - ? results.overrides.lines_to_sample + overrides !== undefined && overrides.lines_to_sample !== undefined + ? overrides.lines_to_sample : DEFAULT_LINES_TO_SAMPLE; return { @@ -125,8 +156,8 @@ export function processResults(results) { * @param {string} indexName * @returns {Promise} */ -export async function hasImportPermission(indexName) { - const priv = { +export async function hasImportPermission(indexName: string) { + const priv: { cluster: string[]; index?: any } = { cluster: ['cluster:monitor/nodes/info', 'cluster:admin/ingest/pipeline/put'], }; diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/datavisualizer.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/datavisualizer.ts index 9b492530d303d2..20332546d9cdec 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/datavisualizer.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/datavisualizer.ts @@ -7,6 +7,7 @@ import { http } from '../http_service'; import { basePath } from './index'; +import { ImportResponse } from '../../../../common/types/file_datavisualizer'; export const fileDatavisualizer = { analyzeFile(file: string, params: Record = {}) { @@ -27,7 +28,7 @@ export const fileDatavisualizer = { mappings, ingestPipeline, }: { - id: string; + id: string | undefined; index: string; data: any; settings: any; @@ -43,7 +44,7 @@ export const fileDatavisualizer = { ingestPipeline, }); - return http({ + return http({ path: `${basePath()}/file_data_visualizer/import`, method: 'POST', query, 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 d5605d3bca65f5..934a0a5e9ae3af 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 8070f94a1264dd..4697496270edf2 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 2472c343d05104..b51be4d2486839 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/client/elasticsearch_ml.ts b/x-pack/plugins/ml/server/client/elasticsearch_ml.ts index caedaed92e5b1b..d5c7882a30d206 100644 --- a/x-pack/plugins/ml/server/client/elasticsearch_ml.ts +++ b/x-pack/plugins/ml/server/client/elasticsearch_ml.ts @@ -740,7 +740,7 @@ export const elasticsearchJsPlugin = (Client: any, config: any, components: any) urls: [ { fmt: - '/_ml/find_file_structure?&charset=<%=charset%>&format=<%=format%>&has_header_row=<%=has_header_row%>&column_names=<%=column_names%>&delimiter=<%=delimiter%>"e=<%=quote%>&should_trim_fields=<%=should_trim_fields%>&grok_pattern=<%=grok_pattern%>×tamp_field=<%=timestamp_field%>×tamp_format=<%=timestamp_format%>&lines_to_sample=<%=lines_to_sample%>', + '/_ml/find_file_structure?&explain=true&charset=<%=charset%>&format=<%=format%>&has_header_row=<%=has_header_row%>&column_names=<%=column_names%>&delimiter=<%=delimiter%>"e=<%=quote%>&should_trim_fields=<%=should_trim_fields%>&grok_pattern=<%=grok_pattern%>×tamp_field=<%=timestamp_field%>×tamp_format=<%=timestamp_format%>&lines_to_sample=<%=lines_to_sample%>', req: { charset: { type: 'string', @@ -778,7 +778,7 @@ export const elasticsearchJsPlugin = (Client: any, config: any, components: any) }, }, { - fmt: '/_ml/find_file_structure', + fmt: '/_ml/find_file_structure?&explain=true', }, ], needBody: true, diff --git a/x-pack/plugins/ml/server/client/error_wrapper.ts b/x-pack/plugins/ml/server/client/error_wrapper.ts index 7f691732954821..de53e4d4345a9b 100644 --- a/x-pack/plugins/ml/server/client/error_wrapper.ts +++ b/x-pack/plugins/ml/server/client/error_wrapper.ts @@ -9,9 +9,13 @@ import { ResponseError, CustomHttpResponseOptions } from 'kibana/server'; export function wrapError(error: any): CustomHttpResponseOptions { const boom = isBoom(error) ? error : boomify(error, { statusCode: error.status }); + const statusCode = boom.output.statusCode; return { - body: boom, + body: { + message: boom, + ...(statusCode !== 500 && error.body ? { attributes: { body: error.body } } : {}), + }, headers: boom.output.headers, - statusCode: boom.output.statusCode, + statusCode, }; } diff --git a/x-pack/plugins/ml/server/config.ts b/x-pack/plugins/ml/server/config.ts new file mode 100644 index 00000000000000..7cef6f17bbefb4 --- /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 175c20bf49c947..6e638d647a3875 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/models/file_data_visualizer/file_data_visualizer.ts b/x-pack/plugins/ml/server/models/file_data_visualizer/file_data_visualizer.ts index 9af755c6918fbe..d53378b886a995 100644 --- a/x-pack/plugins/ml/server/models/file_data_visualizer/file_data_visualizer.ts +++ b/x-pack/plugins/ml/server/models/file_data_visualizer/file_data_visualizer.ts @@ -4,40 +4,21 @@ * you may not use this file except in compliance with the Elastic License. */ -import Boom from 'boom'; import { APICaller } from 'kibana/server'; -import { FindFileStructureResponse } from '../../../common/types/file_datavisualizer'; +import { + AnalysisResult, + FormattedOverrides, + InputOverrides, +} from '../../../common/types/file_datavisualizer'; export type InputData = any[]; -export interface InputOverrides { - [key: string]: string; -} - -export type FormattedOverrides = InputOverrides & { - column_names: string[]; - has_header_row: boolean; - should_trim_fields: boolean; -}; - -export interface AnalysisResult { - results: FindFileStructureResponse; - overrides?: FormattedOverrides; -} - export function fileDataVisualizerProvider(callAsCurrentUser: APICaller) { async function analyzeFile(data: any, overrides: any): Promise { - let results = []; - - try { - results = await callAsCurrentUser('ml.fileStructure', { - body: data, - ...overrides, - }); - } catch (error) { - const err = error.message !== undefined ? error.message : error; - throw Boom.badRequest(err); - } + const results = await callAsCurrentUser('ml.fileStructure', { + body: data, + ...overrides, + }); const { hasOverrides, reducedOverrides } = formatOverrides(overrides); diff --git a/x-pack/plugins/ml/server/models/file_data_visualizer/import_data.ts b/x-pack/plugins/ml/server/models/file_data_visualizer/import_data.ts index ab8c702cbb12ad..9d7009955124f4 100644 --- a/x-pack/plugins/ml/server/models/file_data_visualizer/import_data.ts +++ b/x-pack/plugins/ml/server/models/file_data_visualizer/import_data.ts @@ -6,39 +6,24 @@ import { APICaller } from 'kibana/server'; import { INDEX_META_DATA_CREATED_BY } from '../../../common/constants/file_datavisualizer'; +import { + ImportResponse, + ImportFailure, + Settings, + Mappings, + IngestPipelineWrapper, +} from '../../../common/types/file_datavisualizer'; import { InputData } from './file_data_visualizer'; -export interface Settings { - pipeline?: string; - index: string; - body: any[]; - [key: string]: any; -} - -export interface Mappings { - [key: string]: any; -} - -export interface InjectPipeline { - id: string; - pipeline: any; -} - -interface Failure { - item: number; - reason: string; - doc: any; -} - export function importDataProvider(callAsCurrentUser: APICaller) { async function importData( id: string, index: string, settings: Settings, mappings: Mappings, - ingestPipeline: InjectPipeline, + ingestPipeline: IngestPipelineWrapper, data: InputData - ) { + ): Promise { let createdIndex; let createdPipelineId; const docCount = data.length; @@ -66,7 +51,7 @@ export function importDataProvider(callAsCurrentUser: APICaller) { createdPipelineId = pipelineId; } - let failures: Failure[] = []; + let failures: ImportFailure[] = []; if (data.length) { const resp = await indexData(index, createdPipelineId, data); if (resp.success === false) { @@ -144,7 +129,7 @@ export function importDataProvider(callAsCurrentUser: APICaller) { }; } } catch (error) { - let failures: Failure[] = []; + let failures: ImportFailure[] = []; let ingestError = false; if (error.errors !== undefined && Array.isArray(error.items)) { // an expected error where some or all of the bulk request @@ -169,7 +154,7 @@ export function importDataProvider(callAsCurrentUser: APICaller) { return await callAsCurrentUser('ingest.putPipeline', { id, body: pipeline }); } - function getFailures(items: any[], data: InputData): Failure[] { + function getFailures(items: any[], data: InputData): ImportFailure[] { const failures = []; for (let i = 0; i < items.length; i++) { const item = items[i]; diff --git a/x-pack/plugins/ml/server/models/file_data_visualizer/index.ts b/x-pack/plugins/ml/server/models/file_data_visualizer/index.ts index 94529dc111696e..f8a27fdcd7e1ae 100644 --- a/x-pack/plugins/ml/server/models/file_data_visualizer/index.ts +++ b/x-pack/plugins/ml/server/models/file_data_visualizer/index.ts @@ -4,11 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -export { - fileDataVisualizerProvider, - InputOverrides, - InputData, - AnalysisResult, -} from './file_data_visualizer'; +export { fileDataVisualizerProvider, InputData } from './file_data_visualizer'; -export { importDataProvider, Settings, InjectPipeline, Mappings } from './import_data'; +export { importDataProvider } from './import_data'; diff --git a/x-pack/plugins/ml/server/plugin.ts b/x-pack/plugins/ml/server/plugin.ts index 7d3ef116e67ab7..c7add12be142cb 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 00000000000000..9a5805b8af7ced --- /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 00000000000000..ccff588743af1b --- /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 0773af6e7f070d..92047e300bc1ff 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 93397ff3641ae1..2e5356150086b4 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 ff47d6f2ad4dca..b689d008b51a78 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 2c40ac56e19ec5..00000000000000 --- 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 85357f786ddc17..00000000000000 --- 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 e41b1512f1b299..dcd35b0d323ebf 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 00000000000000..81e375734cc507 --- /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 00000000000000..ae66d603507ca6 --- /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 00000000000000..642ae3c39a0275 --- /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 00000000000000..66ea30d5f2e96a --- /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 78eb9773df15fb..7a9b61f37707ba 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 8ef7339618a2c0..d1513ac16fb158 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 dd6c074e68b1f9..9dcb4ffb82a5fc 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 31a68e8aa9c3ee..5b05c907e796e8 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 00000000000000..a3bcb61afacd61 --- /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 9f7c1d5a994d27..bf6ee965d3b2fb 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 00000000000000..1840a2026a7534 --- /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 00000000000000..83a9e26e4c5890 --- /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 1a2eb1e44be843..b99208bdde2c86 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 41b68d69bbd25f..cfe9f02b9bd6ae 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 c5091c36c3bbeb..1bddede52207b2 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 24d8bcaa4397cf..0fa1a5bf144ac9 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 56922bd8e87e21..d5a43d32f600a9 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 4c109c557fdb0d..1e6c2c4d289aab 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 c98bd73bf83a0a..a392cc96077845 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 75bc1f9eea696d..271d61c2242105 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/searchprofiler/public/application/editor/editor.tsx b/x-pack/plugins/searchprofiler/public/application/editor/editor.tsx index ece22905c64d92..27f040f3e9eeca 100644 --- a/x-pack/plugins/searchprofiler/public/application/editor/editor.tsx +++ b/x-pack/plugins/searchprofiler/public/application/editor/editor.tsx @@ -5,6 +5,8 @@ */ import React, { memo, useRef, useEffect, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiScreenReaderOnly } from '@elastic/eui'; import { Editor as AceEditor } from 'brace'; import { initializeEditor } from './init_editor'; @@ -31,6 +33,8 @@ const createEditorShim = (aceEditor: AceEditor) => { }; }; +const EDITOR_INPUT_ID = 'SearchProfilerTextArea'; + export const Editor = memo(({ licenseEnabled, initialValue, onEditorReady }: Props) => { const containerRef = useRef(null as any); const editorInstanceRef = useRef(null as any); @@ -43,10 +47,25 @@ export const Editor = memo(({ licenseEnabled, initialValue, onEditorReady }: Pro const divEl = containerRef.current; editorInstanceRef.current = initializeEditor({ el: divEl, licenseEnabled }); editorInstanceRef.current.setValue(initialValue, 1); + const textarea = divEl.querySelector('textarea'); + if (textarea) { + textarea.setAttribute('id', EDITOR_INPUT_ID); + } setTextArea(licenseEnabled ? containerRef.current!.querySelector('textarea') : null); onEditorReady(createEditorShim(editorInstanceRef.current)); }, [initialValue, onEditorReady, licenseEnabled]); - return
; + return ( + <> + + + +
+ + ); }); diff --git a/x-pack/plugins/security/server/authorization/index.ts b/x-pack/plugins/security/server/authorization/index.ts index f065c9cfd90bac..cf970a561b93f9 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 3d25fc03f568b6..b023c12d35b79a 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 b25aad30a34238..9a8935f80a174f 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 ac386d287cff19..cd2c7faa263c96 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 00000000000000..26af0dadfb2886 --- /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 00000000000000..0915308fc0f894 --- /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 7809b511adda49..99b4e184c071a6 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 4d92505c4aebbc..fee41fc7e36d32 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 b22cec0af5ea83..4f6ff55dbfbb2d 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 96cbac4b48065a..ea74fc92b95ead 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 fb2616619c644b..65a0cabfeb7166 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 96e642b0f45d8b..44c9e9993bf102 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/task_manager/server/README.md b/x-pack/plugins/task_manager/server/README.md index a4154f3ecf2124..c3d45be5d8f22e 100644 --- a/x-pack/plugins/task_manager/server/README.md +++ b/x-pack/plugins/task_manager/server/README.md @@ -456,6 +456,6 @@ The task manager's public API is create / delete / list. Updates aren't directly ``` - Integration tests: ``` - node scripts/functional_tests_server.js --config x-pack/test/plugin_api_integration/config.js - node scripts/functional_test_runner --config x-pack/test/plugin_api_integration/config.js + node scripts/functional_tests_server.js --config x-pack/test/plugin_api_integration/config.ts + node scripts/functional_test_runner --config x-pack/test/plugin_api_integration/config.ts ``` diff --git a/x-pack/plugins/transform/public/app/components/pivot_preview/pivot_preview.tsx b/x-pack/plugins/transform/public/app/components/pivot_preview/pivot_preview.tsx index c0c85f74418fce..c50df0366d6987 100644 --- a/x-pack/plugins/transform/public/app/components/pivot_preview/pivot_preview.tsx +++ b/x-pack/plugins/transform/public/app/components/pivot_preview/pivot_preview.tsx @@ -245,6 +245,10 @@ export const PivotPreview: FC = React.memo( return formatHumanReadableDateTimeSeconds(moment(cellValue).unix() * 1000); } + if (previewMappings.properties[columnId].type === ES_FIELD_TYPES.BOOLEAN) { + return cellValue ? 'true' : 'false'; + } + return cellValue; }; }, [pageData, pagination.pageIndex, pagination.pageSize, previewMappings.properties]); diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/source_index_preview/source_index_preview.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/source_index_preview/source_index_preview.tsx index c56263b7210329..bcdeb7ddb0d365 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/source_index_preview/source_index_preview.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/source_index_preview/source_index_preview.tsx @@ -105,6 +105,9 @@ export const SourceIndexPreview: React.FC = React.memo(({ indexPattern, q let schema; switch (field?.type) { + case KBN_FIELD_TYPES.BOOLEAN: + schema = 'boolean'; + break; case KBN_FIELD_TYPES.DATE: schema = 'datetime'; break; @@ -190,6 +193,10 @@ export const SourceIndexPreview: React.FC = React.memo(({ indexPattern, q return formatHumanReadableDateTimeSeconds(moment(cellValue).unix() * 1000); } + if (field?.type === KBN_FIELD_TYPES.BOOLEAN) { + return cellValue ? 'true' : 'false'; + } + return cellValue; }; }, [data, indexPattern.fields, pagination.pageIndex, pagination.pageSize]); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 023a97274b957b..e63e1c8ad2c916 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 の管理を行うセンターコンソールです。", @@ -4531,7 +4529,6 @@ "xpack.apm.settings.apmIndices.spanIndicesLabel": "スパンインデックス", "xpack.apm.settings.apmIndices.title": "インデックス", "xpack.apm.settings.apmIndices.transactionIndicesLabel": "トランザクションインデックス", - "xpack.apm.settings.customizeUI": "UI をカスタマイズ", "xpack.apm.settings.customizeUI.customLink": "カスタムリンク", "xpack.apm.settings.customizeUI.customLink.create.failed": "リンクを保存できませんでした!", "xpack.apm.settings.customizeUI.customLink.create.failed.message": "リンクを保存するときに問題が発生しました。エラー: 「{errorMessage}」", @@ -9733,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 インデックスのデータを可視化します。", @@ -9984,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": "実際値が通常値と同じ", @@ -13548,9 +13543,7 @@ "xpack.siem.components.mlPopover.jobsTable.filters.searchFilterPlaceholder": "例: rare_process_linux", "xpack.siem.components.mlPopover.jobsTable.filters.showAllJobsLabel": "Elastic ジョブ", "xpack.siem.components.mlPopover.jobsTable.filters.showSiemJobsLabel": "カスタムジョブ", - "xpack.siem.components.mlPopup.anomalyDetectionButtonLabel": "異常検知", "xpack.siem.components.mlPopup.anomalyDetectionDescription": "下のいずれかの機械学習ジョブを実行して、SIEM アプリケーション全体の異常イベントを表示することができます。始めに使えるように、いくつかの一般的な検出ジョブが提供されています。独自のカスタムジョブを追加する場合は、{machineLearning} アプリケーションでジョブを作成して「SIEM」でタグ付けすると、ここに追加されます。", - "xpack.siem.components.mlPopup.anomalyDetectionTitle": "異常検知設定", "xpack.siem.components.mlPopup.cloudLink": "クラウド展開", "xpack.siem.components.mlPopup.errors.createJobFailureTitle": "ジョブ作成エラー", "xpack.siem.components.mlPopup.errors.startJobFailureTitle": "ジョブ開始エラー", @@ -15871,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": "サーバーからメールを送信します。", @@ -15967,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}}}を削除 ", @@ -15993,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": "このコネクターを削除", @@ -16044,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": "下の表現のエラーを修正してください。", @@ -16081,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} コネクター", @@ -16133,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 b359014e95e70c..cc75ceb988d972 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 的中心控制台。", @@ -4532,7 +4530,6 @@ "xpack.apm.settings.apmIndices.spanIndicesLabel": "跨度索引", "xpack.apm.settings.apmIndices.title": "索引", "xpack.apm.settings.apmIndices.transactionIndicesLabel": "事务索引", - "xpack.apm.settings.customizeUI": "定制 UI", "xpack.apm.settings.customizeUI.customLink": "定制链接", "xpack.apm.settings.customizeUI.customLink.create.failed": "链接无法保存!", "xpack.apm.settings.customizeUI.customLink.create.failed.message": "保存链接时出现了问题。错误:“{errorMessage}”", @@ -9736,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 索引中的数据。", @@ -9987,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": "实际上与典型模式相同", @@ -13552,9 +13547,7 @@ "xpack.siem.components.mlPopover.jobsTable.filters.searchFilterPlaceholder": "例如 rare_process_linux", "xpack.siem.components.mlPopover.jobsTable.filters.showAllJobsLabel": "Elastic 作业", "xpack.siem.components.mlPopover.jobsTable.filters.showSiemJobsLabel": "定制作业", - "xpack.siem.components.mlPopup.anomalyDetectionButtonLabel": "异常检测", "xpack.siem.components.mlPopup.anomalyDetectionDescription": "运行以下任何 Machine Learning 作业以查看该 SIEM 应用程序的所有异常事件。我们提供若干可让您入门的常规检测作业。如果您希望添加自己的定制作业,只需从用于纳入定制作业的 {machineLearning} 应用程序中创建定制作业并使用“SIEM”标记它们。", - "xpack.siem.components.mlPopup.anomalyDetectionTitle": "异常检测设置", "xpack.siem.components.mlPopup.cloudLink": "云部署", "xpack.siem.components.mlPopup.errors.createJobFailureTitle": "创建作业失败", "xpack.siem.components.mlPopup.errors.startJobFailureTitle": "启动作业失败", @@ -15875,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": "从您的服务器发送电子邮件。", @@ -15971,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}}} ", @@ -15998,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": "删除此连接器", @@ -16049,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": "表达式包含错误。", @@ -16086,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} 连接器", @@ -16138,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 85699cfbd750f0..00000000000000 --- 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 f7a80202dff89a..00000000000000 --- 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 a7d479f922ed1b..af9e34071fd095 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 6ce954f61bcdbc..d83396f2008298 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 d631882e1f581c..56d9f40e40021d 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 fd35db43042759..84d8b6e8caeded 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 fdb60bd2d91466..c4489a316d2c00 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 00000000000000..c4d12221e3a01e --- /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 00000000000000..5156a6146f3a12 --- /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 00000000000000..c967cf5de0771c --- /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 00000000000000..fe001ce294ef4f --- /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 00000000000000..0e956ea56faa9c --- /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 00000000000000..df593d587de3fb --- /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 28bc02ec3392f2..00000000000000 --- 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 9874a3a0697d2c..00000000000000 --- 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 7c8d798984bf27..4d0a9980f2231f 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 ee68b7e269c342..e9cf2a270d180d 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 26ad97f05849d4..e82d268accdd82 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 00000000000000..b677924724ffe8 --- /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 89d37c4d00a112..41564146bb84d9 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 3ade4e6368f96e..6b011ac84bc6f1 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 f9aa2cad8bfc60..2c063ea6b4fa64 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 df7e5d8fe9a78d..e469a501089128 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 3d65b8a799b1b1..70ad1cae6c1d14 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 9331fe17046949..4fa1e7e4c6e4df 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 fc07171347e5e5..47e058f4739463 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 e44e20751b315e..0620ced6365a96 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 6fcfb463c4c778..916ba368e07320 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 3f27a7860bafa0..4255eca83be473 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 108cc724aa4074..66aa02e1930a3e 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 afd3299f0c2bbc..5d59180ff572b0 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 fb3ff9ceb09265..99dd7b63383fbf 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 7dfaa7b918f705..7f78d327d01225 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/uptime/server/lib/adapters/telemetry/kibana_telemetry_adapter.ts b/x-pack/plugins/uptime/server/lib/adapters/telemetry/kibana_telemetry_adapter.ts index 3b2696ba23f7c9..fa792429f102e6 100644 --- a/x-pack/plugins/uptime/server/lib/adapters/telemetry/kibana_telemetry_adapter.ts +++ b/x-pack/plugins/uptime/server/lib/adapters/telemetry/kibana_telemetry_adapter.ts @@ -5,7 +5,7 @@ */ import moment from 'moment'; -import { ISavedObjectsRepository } from 'kibana/server'; +import { ISavedObjectsRepository, SavedObjectsClientContract } from 'kibana/server'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { PageViewParams, UptimeTelemetry } from './types'; import { APICaller } from '../framework'; @@ -54,6 +54,9 @@ export class KibanaTelemetryAdapter { } public static countPageView(pageView: PageViewParams) { + if (pageView.refreshTelemetryHistory) { + this.collector = {}; + } const bucketId = this.getBucketToIncrement(); const bucket = this.collector[bucketId]; if (pageView.page === 'Overview') { @@ -94,7 +97,7 @@ export class KibanaTelemetryAdapter { public static async countNoOfUniqueMonitorAndLocations( callCluster: APICaller, - savedObjectsClient: ISavedObjectsRepository + savedObjectsClient: ISavedObjectsRepository | SavedObjectsClientContract ) { const dynamicSettings = await savedObjectsAdapter.getUptimeDynamicSettings(savedObjectsClient); const params = { @@ -161,24 +164,27 @@ export class KibanaTelemetryAdapter { const monitorNameStats: any = result?.aggregations?.monitor_name; const locationNameStats: any = result?.aggregations?.observer_loc_name; const uniqueMonitors: any = result?.aggregations?.monitors.buckets; - const bucket = this.getBucketToIncrement(); - this.collector[bucket].no_of_unique_monitors = numberOfUniqueMonitors; - this.collector[bucket].no_of_unique_observer_locations = numberOfUniqueLocations; - this.collector[bucket].no_of_unique_observer_locations = numberOfUniqueLocations; - this.collector[bucket].monitor_name_stats = { + const bucketId = this.getBucketToIncrement(); + const bucket = this.collector[bucketId]; + + bucket.no_of_unique_monitors = numberOfUniqueMonitors; + bucket.no_of_unique_observer_locations = numberOfUniqueLocations; + bucket.no_of_unique_observer_locations = numberOfUniqueLocations; + bucket.monitor_name_stats = { min_length: monitorNameStats?.min_length ?? 0, max_length: monitorNameStats?.max_length ?? 0, - avg_length: +monitorNameStats?.avg_length.toFixed(2), + avg_length: +(monitorNameStats?.avg_length?.toFixed(2) ?? 0), }; - this.collector[bucket].observer_location_name_stats = { + bucket.observer_location_name_stats = { min_length: locationNameStats?.min_length ?? 0, max_length: locationNameStats?.max_length ?? 0, avg_length: +(locationNameStats?.avg_length?.toFixed(2) ?? 0), }; - this.collector[bucket].monitor_frequency = this.getMonitorsFrequency(uniqueMonitors); + bucket.monitor_frequency = this.getMonitorsFrequency(uniqueMonitors); + return bucket; } private static getMonitorsFrequency(uniqueMonitors = []) { diff --git a/x-pack/plugins/uptime/server/lib/adapters/telemetry/types.ts b/x-pack/plugins/uptime/server/lib/adapters/telemetry/types.ts index 059bea6cc3215c..ee3360ecc41b18 100644 --- a/x-pack/plugins/uptime/server/lib/adapters/telemetry/types.ts +++ b/x-pack/plugins/uptime/server/lib/adapters/telemetry/types.ts @@ -10,6 +10,7 @@ export interface PageViewParams { dateEnd: string; autoRefreshEnabled: boolean; autorefreshInterval: number; + refreshTelemetryHistory?: boolean; } export interface Stats { diff --git a/x-pack/plugins/uptime/server/lib/requests/search/refine_potential_matches.ts b/x-pack/plugins/uptime/server/lib/requests/search/refine_potential_matches.ts index 7d69ff6751f055..218eb2f121a81d 100644 --- a/x-pack/plugins/uptime/server/lib/requests/search/refine_potential_matches.ts +++ b/x-pack/plugins/uptime/server/lib/requests/search/refine_potential_matches.ts @@ -68,7 +68,8 @@ const fullyMatchingIds = async ( const status = topSource.summary.down > 0 ? 'down' : 'up'; // This monitor doesn't match, so just skip ahead and don't add it to the output - if (queryContext.statusFilter && queryContext.statusFilter !== status) { + // Only skip in case of up statusFilter, for a monitor to be up, all checks should be up + if (queryContext?.statusFilter === 'up' && queryContext.statusFilter !== status) { continue MonitorLoop; } diff --git a/x-pack/plugins/uptime/server/rest_api/create_route_with_auth.ts b/x-pack/plugins/uptime/server/rest_api/create_route_with_auth.ts index 41527d76432cb4..966dc20e27a7ed 100644 --- a/x-pack/plugins/uptime/server/rest_api/create_route_with_auth.ts +++ b/x-pack/plugins/uptime/server/rest_api/create_route_with_auth.ts @@ -16,7 +16,7 @@ export const createRouteWithAuth = ( const licenseCheckHandler: UMRouteHandler = async (customParams, context, request, response) => { const { statusCode, message } = libs.license(context.licensing.license); if (statusCode === 200) { - return await handler(customParams, context, request, response); + return handler(customParams, context, request, response); } switch (statusCode) { case 400: diff --git a/x-pack/plugins/uptime/server/rest_api/dynamic_settings.ts b/x-pack/plugins/uptime/server/rest_api/dynamic_settings.ts index 2235379ba6f038..3f4e2fc345182c 100644 --- a/x-pack/plugins/uptime/server/rest_api/dynamic_settings.ts +++ b/x-pack/plugins/uptime/server/rest_api/dynamic_settings.ts @@ -19,9 +19,6 @@ export const createGetDynamicSettingsRoute: UMRestApiRouteFactory = (libs: UMSer method: 'GET', path: '/api/uptime/dynamic_settings', validate: false, - options: { - tags: ['access:uptime-read'], - }, handler: async ({ dynamicSettings }, _context, _request, response): Promise => { return response.ok({ body: dynamicSettings, @@ -35,6 +32,7 @@ export const createPostDynamicSettingsRoute: UMRestApiRouteFactory = (libs: UMSe validate: { body: schema.object({}, { unknowns: 'allow' }), }, + writeAccess: true, options: { tags: ['access:uptime-write'], }, diff --git a/x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts index cec5bb1be245f1..689a75c5903a64 100644 --- a/x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts +++ b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_pattern.ts @@ -12,9 +12,6 @@ export const createGetIndexPatternRoute: UMRestApiRouteFactory = (libs: UMServer method: 'GET', path: API_URLS.INDEX_PATTERN, validate: false, - options: { - tags: ['access:uptime-read'], - }, handler: async ({ callES, dynamicSettings }, _context, _request, response): Promise => { try { return response.ok({ diff --git a/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts index 9c94ef92f9b6ed..8ed73d90b2389e 100644 --- a/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts +++ b/x-pack/plugins/uptime/server/rest_api/index_state/get_index_status.ts @@ -12,9 +12,6 @@ export const createGetIndexStatusRoute: UMRestApiRouteFactory = (libs: UMServerL method: 'GET', path: API_URLS.INDEX_STATUS, validate: false, - options: { - tags: ['access:uptime-read'], - }, handler: async ({ callES, dynamicSettings }, _context, _request, response): Promise => { try { return response.ok({ diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/index.ts b/x-pack/plugins/uptime/server/rest_api/monitors/index.ts index 7da717b50c1496..51b39037049b58 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/index.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/index.ts @@ -6,4 +6,4 @@ export { createGetMonitorDetailsRoute } from './monitors_details'; export { createGetMonitorLocationsRoute } from './monitor_locations'; -export { createGetStatusBarRoute } from './status'; +export { createGetStatusBarRoute } from './monitor_status'; diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/monitor_locations.ts b/x-pack/plugins/uptime/server/rest_api/monitors/monitor_locations.ts index befa5fd7e0e55d..66ce9871506d4e 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/monitor_locations.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/monitor_locations.ts @@ -19,9 +19,6 @@ export const createGetMonitorLocationsRoute: UMRestApiRouteFactory = (libs: UMSe dateEnd: schema.string(), }), }, - options: { - tags: ['access:uptime-read'], - }, handler: async ({ callES, dynamicSettings }, _context, request, response): Promise => { const { monitorId, dateStart, dateEnd } = request.query; diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/status.ts b/x-pack/plugins/uptime/server/rest_api/monitors/monitor_status.ts similarity index 95% rename from x-pack/plugins/uptime/server/rest_api/monitors/status.ts rename to x-pack/plugins/uptime/server/rest_api/monitors/monitor_status.ts index 9bccd64c4bd659..9cf1340fb94090 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/status.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/monitor_status.ts @@ -20,9 +20,6 @@ export const createGetStatusBarRoute: UMRestApiRouteFactory = (libs: UMServerLib dateEnd: schema.string(), }), }, - options: { - tags: ['access:uptime-read'], - }, handler: async ({ callES, dynamicSettings }, _context, request, response): Promise => { const { monitorId, dateStart, dateEnd } = request.query; const result = await libs.requests.getLatestMonitor({ diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/monitors_details.ts b/x-pack/plugins/uptime/server/rest_api/monitors/monitors_details.ts index b14eb2c138a751..1cc010781457e2 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/monitors_details.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/monitors_details.ts @@ -19,9 +19,6 @@ export const createGetMonitorDetailsRoute: UMRestApiRouteFactory = (libs: UMServ dateEnd: schema.maybe(schema.string()), }), }, - options: { - tags: ['access:uptime-read'], - }, handler: async ({ callES, dynamicSettings }, _context, request, response): Promise => { const { monitorId, dateStart, dateEnd } = request.query; return response.ok({ diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/monitors_durations.ts b/x-pack/plugins/uptime/server/rest_api/monitors/monitors_durations.ts index 10008c4f6c7eaf..9743ced13350a4 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/monitors_durations.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/monitors_durations.ts @@ -20,9 +20,6 @@ export const createGetMonitorDurationRoute: UMRestApiRouteFactory = (libs: UMSer dateEnd: schema.string(), }), }, - options: { - tags: ['access:uptime-read'], - }, handler: async ({ callES, dynamicSettings }, _context, request, response): Promise => { const { monitorId, dateStart, dateEnd } = request.query; return response.ok({ diff --git a/x-pack/plugins/uptime/server/rest_api/overview_filters/get_overview_filters.ts b/x-pack/plugins/uptime/server/rest_api/overview_filters/get_overview_filters.ts index 05376f061c05f3..deac05f36c8dc9 100644 --- a/x-pack/plugins/uptime/server/rest_api/overview_filters/get_overview_filters.ts +++ b/x-pack/plugins/uptime/server/rest_api/overview_filters/get_overview_filters.ts @@ -28,10 +28,6 @@ export const createGetOverviewFilters: UMRestApiRouteFactory = (libs: UMServerLi tags: arrayOrStringType, }), }, - - options: { - tags: ['access:uptime-read'], - }, handler: async ({ callES, dynamicSettings }, _context, request, response) => { const { dateRangeStart, dateRangeEnd, locations, schemes, search, ports, tags } = request.query; diff --git a/x-pack/plugins/uptime/server/rest_api/pings/get_all.ts b/x-pack/plugins/uptime/server/rest_api/pings/get_all.ts index d64c76fc18a809..c76892103da6b7 100644 --- a/x-pack/plugins/uptime/server/rest_api/pings/get_all.ts +++ b/x-pack/plugins/uptime/server/rest_api/pings/get_all.ts @@ -23,9 +23,6 @@ export const createGetAllRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => status: schema.maybe(schema.string()), }), }, - options: { - tags: ['access:uptime-read'], - }, handler: async ({ callES, dynamicSettings }, _context, request, response): Promise => { const { dateRangeStart, dateRangeEnd, location, monitorId, size, sort, status } = request.query; diff --git a/x-pack/plugins/uptime/server/rest_api/pings/get_ping_histogram.ts b/x-pack/plugins/uptime/server/rest_api/pings/get_ping_histogram.ts index cbd9ada027b31f..dceef5ecb78486 100644 --- a/x-pack/plugins/uptime/server/rest_api/pings/get_ping_histogram.ts +++ b/x-pack/plugins/uptime/server/rest_api/pings/get_ping_histogram.ts @@ -21,9 +21,6 @@ export const createGetPingHistogramRoute: UMRestApiRouteFactory = (libs: UMServe filters: schema.maybe(schema.string()), }), }, - options: { - tags: ['access:uptime-read'], - }, handler: async ({ callES, dynamicSettings }, _context, request, response): Promise => { const { dateStart, dateEnd, statusFilter, monitorId, filters } = request.query; diff --git a/x-pack/plugins/uptime/server/rest_api/pings/get_pings.ts b/x-pack/plugins/uptime/server/rest_api/pings/get_pings.ts index 8129ad70e6d7da..cde9a8c4e47ead 100644 --- a/x-pack/plugins/uptime/server/rest_api/pings/get_pings.ts +++ b/x-pack/plugins/uptime/server/rest_api/pings/get_pings.ts @@ -23,9 +23,6 @@ export const createGetPingsRoute: UMRestApiRouteFactory = (libs: UMServerLibs) = status: schema.maybe(schema.string()), }), }, - options: { - tags: ['access:uptime-read'], - }, handler: async ({ callES, dynamicSettings }, _context, request, response): Promise => { const { dateRangeStart, dateRangeEnd, location, monitorId, size, sort, status } = request.query; diff --git a/x-pack/plugins/uptime/server/rest_api/snapshot/get_snapshot_count.ts b/x-pack/plugins/uptime/server/rest_api/snapshot/get_snapshot_count.ts index 4fda95bbf86da9..d870f492801171 100644 --- a/x-pack/plugins/uptime/server/rest_api/snapshot/get_snapshot_count.ts +++ b/x-pack/plugins/uptime/server/rest_api/snapshot/get_snapshot_count.ts @@ -7,7 +7,7 @@ import { schema } from '@kbn/config-schema'; import { UMServerLibs } from '../../lib/lib'; import { UMRestApiRouteFactory } from '../types'; -import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants/rest_api'; +import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants'; export const createGetSnapshotCount: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', @@ -20,9 +20,6 @@ export const createGetSnapshotCount: UMRestApiRouteFactory = (libs: UMServerLibs statusFilter: schema.maybe(schema.string()), }), }, - options: { - tags: ['access:uptime-read'], - }, handler: async ({ callES, dynamicSettings }, _context, request, response): Promise => { const { dateRangeStart, dateRangeEnd, filters, statusFilter } = request.query; const result = await libs.requests.getSnapshotCount({ diff --git a/x-pack/plugins/uptime/server/rest_api/telemetry/log_page_view.ts b/x-pack/plugins/uptime/server/rest_api/telemetry/log_page_view.ts index 1f6f0520198708..4b2db71037071e 100644 --- a/x-pack/plugins/uptime/server/rest_api/telemetry/log_page_view.ts +++ b/x-pack/plugins/uptime/server/rest_api/telemetry/log_page_view.ts @@ -8,10 +8,11 @@ import { schema } from '@kbn/config-schema'; import { KibanaTelemetryAdapter } from '../../lib/adapters/telemetry'; import { UMRestApiRouteFactory } from '../types'; import { PageViewParams } from '../../lib/adapters/telemetry/types'; +import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants'; export const createLogPageViewRoute: UMRestApiRouteFactory = () => ({ method: 'POST', - path: '/api/uptime/logPageView', + path: API_URLS.LOG_PAGE_VIEW, validate: { body: schema.object({ page: schema.string(), @@ -19,15 +20,20 @@ export const createLogPageViewRoute: UMRestApiRouteFactory = () => ({ dateEnd: schema.string(), autoRefreshEnabled: schema.boolean(), autorefreshInterval: schema.number(), + refreshTelemetryHistory: schema.maybe(schema.boolean()), }), }, - handler: async ({ callES, dynamicSettings }, _context, _request, response): Promise => { - const result = KibanaTelemetryAdapter.countPageView(_request.body as PageViewParams); + handler: async ( + { savedObjectsClient, callES, dynamicSettings }, + _context, + request, + response + ): Promise => { + await KibanaTelemetryAdapter.countNoOfUniqueMonitorAndLocations(callES, savedObjectsClient); + const pageViewResult = KibanaTelemetryAdapter.countPageView(request.body as PageViewParams); + return response.ok({ - body: result, + body: pageViewResult, }); }, - options: { - tags: ['access:uptime-read'], - }, }); diff --git a/x-pack/plugins/uptime/server/rest_api/types.ts b/x-pack/plugins/uptime/server/rest_api/types.ts index 8bb1e8a6a86c0b..aecb099b7bed5c 100644 --- a/x-pack/plugins/uptime/server/rest_api/types.ts +++ b/x-pack/plugins/uptime/server/rest_api/types.ts @@ -24,6 +24,7 @@ import { UMServerLibs } from '../lib/lib'; */ export interface UMServerRoute { method: string; + writeAccess?: boolean; handler: T; } diff --git a/x-pack/plugins/uptime/server/rest_api/uptime_route_wrapper.ts b/x-pack/plugins/uptime/server/rest_api/uptime_route_wrapper.ts index 676aced23a25e5..7ede9d39c5f2c5 100644 --- a/x-pack/plugins/uptime/server/rest_api/uptime_route_wrapper.ts +++ b/x-pack/plugins/uptime/server/rest_api/uptime_route_wrapper.ts @@ -7,21 +7,20 @@ import { UMKibanaRouteWrapper } from './types'; import { savedObjectsAdapter } from '../lib/saved_objects'; -export const uptimeRouteWrapper: UMKibanaRouteWrapper = uptimeRoute => { - return { - ...uptimeRoute, - handler: async (context, request, response) => { - const { callAsCurrentUser: callES } = context.core.elasticsearch.dataClient; - const { client: savedObjectsClient } = context.core.savedObjects; - const dynamicSettings = await savedObjectsAdapter.getUptimeDynamicSettings( - savedObjectsClient - ); - return await uptimeRoute.handler( - { callES, savedObjectsClient, dynamicSettings }, - context, - request, - response - ); - }, - }; -}; +export const uptimeRouteWrapper: UMKibanaRouteWrapper = uptimeRoute => ({ + ...uptimeRoute, + options: { + tags: ['access:uptime-read', ...(uptimeRoute?.writeAccess ? ['access:uptime-write'] : [])], + }, + handler: async (context, request, response) => { + const { callAsCurrentUser: callES } = context.core.elasticsearch.dataClient; + const { client: savedObjectsClient } = context.core.savedObjects; + const dynamicSettings = await savedObjectsAdapter.getUptimeDynamicSettings(savedObjectsClient); + return uptimeRoute.handler( + { callES, savedObjectsClient, dynamicSettings }, + context, + request, + response + ); + }, +}); diff --git a/x-pack/plugins/watcher/public/plugin.ts b/x-pack/plugins/watcher/public/plugin.ts index 9bee7e60273e48..6de21bc27d48c7 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/scripts/functional_tests.js b/x-pack/scripts/functional_tests.js index b0ca33b00fde83..7943da07716a13 100644 --- a/x-pack/scripts/functional_tests.js +++ b/x-pack/scripts/functional_tests.js @@ -17,7 +17,7 @@ const onlyNotInCoverageTests = [ require.resolve('../test/alerting_api_integration/spaces_only/config.ts'), require.resolve('../test/alerting_api_integration/security_and_spaces/config.ts'), require.resolve('../test/detection_engine_api_integration/security_and_spaces/config.ts'), - require.resolve('../test/plugin_api_integration/config.js'), + require.resolve('../test/plugin_api_integration/config.ts'), require.resolve('../test/plugin_functional/config.ts'), require.resolve('../test/kerberos_api_integration/config.ts'), require.resolve('../test/kerberos_api_integration/anonymous_access.config.ts'), diff --git a/x-pack/test/accessibility/apps/home.ts b/x-pack/test/accessibility/apps/home.ts index f40976f09f9c88..463673d966c2e5 100644 --- a/x-pack/test/accessibility/apps/home.ts +++ b/x-pack/test/accessibility/apps/home.ts @@ -52,14 +52,12 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { await a11y.testAppSnapshot(); }); - // issue - logo images are missing alt -text https://github.com/elastic/kibana/issues/62239 - it.skip('click on ActiveMQ logs panel to open tutorial meets a11y requirements', async () => { + it('click on ActiveMQ logs panel to open tutorial meets a11y requirements', async () => { await PageObjects.home.clickOnLogsTutorial(); await a11y.testAppSnapshot(); }); - // https://github.com/elastic/kibana/issues/62239 - it.skip('click on cloud tutorial meets a11y requirements', async () => { + it('click on cloud tutorial meets a11y requirements', async () => { await PageObjects.home.clickOnCloudTutorial(); await a11y.testAppSnapshot(); }); diff --git a/x-pack/test/alerting_api_integration/common/config.ts b/x-pack/test/alerting_api_integration/common/config.ts index 5fb1afa7d584f2..4d32a5ae9f53c4 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 e228f6c1f81c6f..6001dd531cfae8 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 01eaf92da33fe5..612eba858ea0b0 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 cfc04663c6a4f3..eeb0818b5fbabd 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 f4ea568cf08c34..e9d3e6c542442b 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 48f348e1b834d8..054f8f6141817e 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 8afa43bfea21e6..e00589b7e85b7b 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 da83dbf8c47e26..fd996ea4507bab 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 43a3861491467f..922315eba5a5c6 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 6fca330887c3e6..011e47cf11b394 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 bed4c805aaf573..c84b089d48c85e 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 89c5b4f451f82b..80b512f3fb5e3f 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 c6960a4eedd256..d7ec2e78ccb308 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 a792efede07ee0..6cafbeb8c6ea8b 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 3713e9c24419f7..874d42ac04736a 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 efd707b59cd343..c70c289194abbd 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 283e51352c2726..26a811d2cc512e 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 acbc9edc1f2fba..00000000000000 --- 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 0f896bfaa0af9b..a4a13441fb7668 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 00000000000000..517c64f178af5b --- /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 fb2be8c86f4e8b..75544b7fd41697 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 18a0ecc23c1e14..2593f342a8a862 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 fb0c5e13c0720d..05d26aaaed2ec3 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 ad495d45054041..887be6b85b100c 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/fleet/agents/acks.ts b/x-pack/test/api_integration/apis/fleet/agents/acks.ts index a2eba2c23c39d6..f08ce33d8b60f0 100644 --- a/x-pack/test/api_integration/apis/fleet/agents/acks.ts +++ b/x-pack/test/api_integration/apis/fleet/agents/acks.ts @@ -178,7 +178,7 @@ export default function(providerContext: FtrProviderContext) { ], }) .expect(400); - expect(apiResponse.message).to.eql('all actions should belong to current agent'); + expect(apiResponse.message).to.eql('One or more actions cannot be found'); }); it('should return a 400 when request event list contains action types that are not allowed for acknowledgement', async () => { diff --git a/x-pack/test/api_integration/apis/fleet/agents/actions.ts b/x-pack/test/api_integration/apis/fleet/agents/actions.ts index f27b932cff5cba..cf0641acf9e1cd 100644 --- a/x-pack/test/api_integration/apis/fleet/agents/actions.ts +++ b/x-pack/test/api_integration/apis/fleet/agents/actions.ts @@ -28,28 +28,15 @@ export default function(providerContext: FtrProviderContext) { .send({ action: { type: 'CONFIG_CHANGE', - data: 'action_data', + data: { data: 'action_data' }, sent_at: '2020-03-18T19:45:02.620Z', }, }) .expect(200); expect(apiResponse.success).to.be(true); - expect(apiResponse.item.data).to.be('action_data'); + expect(apiResponse.item.data).to.eql({ data: 'action_data' }); expect(apiResponse.item.sent_at).to.be('2020-03-18T19:45:02.620Z'); - - const { body: agentResponse } = await supertest - .get(`/api/ingest_manager/fleet/agents/agent1`) - .set('kbn-xsrf', 'xx') - .expect(200); - - const updatedAction = agentResponse.item.actions.find( - (itemAction: Record) => itemAction?.data === 'action_data' - ); - - expect(updatedAction.type).to.be('CONFIG_CHANGE'); - expect(updatedAction.data).to.be('action_data'); - expect(updatedAction.sent_at).to.be('2020-03-18T19:45:02.620Z'); }); it('should return a 400 when request does not have type information', async () => { @@ -58,7 +45,7 @@ export default function(providerContext: FtrProviderContext) { .set('kbn-xsrf', 'xx') .send({ action: { - data: 'action_data', + data: { data: 'action_data' }, sent_at: '2020-03-18T19:45:02.620Z', }, }) @@ -75,7 +62,7 @@ export default function(providerContext: FtrProviderContext) { .send({ action: { type: 'CONFIG_CHANGE', - data: 'action_data', + data: { data: 'action_data' }, sent_at: '2020-03-18T19:45:02.620Z', }, }) 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 9cb2e5ee54596f..e41dd40bbd4179 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 77293ddff3f9f6..9bec3fd076e865 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 0b29fc1cac7de7..1f9eac148b3029 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 0b147022c7cd5c..e0db91449a8ccd 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/api_integration/apis/uptime/get_all_pings.js b/x-pack/test/api_integration/apis/uptime/get_all_pings.ts similarity index 95% rename from x-pack/test/api_integration/apis/uptime/get_all_pings.js rename to x-pack/test/api_integration/apis/uptime/get_all_pings.ts index dcbc9389b1da24..666986e7008b7c 100644 --- a/x-pack/test/api_integration/apis/uptime/get_all_pings.js +++ b/x-pack/test/api_integration/apis/uptime/get_all_pings.ts @@ -7,8 +7,9 @@ import moment from 'moment'; import expect from '@kbn/expect'; import { PINGS_DATE_RANGE_START, PINGS_DATE_RANGE_END } from './constants'; +import { FtrProviderContext } from '../../ftr_provider_context'; -export default function({ getService }) { +export default function({ getService }: FtrProviderContext) { const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); diff --git a/x-pack/test/api_integration/apis/uptime/graphql/index.js b/x-pack/test/api_integration/apis/uptime/graphql/index.ts similarity index 82% rename from x-pack/test/api_integration/apis/uptime/graphql/index.js rename to x-pack/test/api_integration/apis/uptime/graphql/index.ts index ee22974d471701..2e0b5e2eea2a5f 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/index.js +++ b/x-pack/test/api_integration/apis/uptime/graphql/index.ts @@ -4,7 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -export default function({ loadTestFile }) { +import { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function({ loadTestFile }: FtrProviderContext) { describe('graphql', () => { // each of these test files imports a GQL query from // the uptime app and runs it against the live HTTP server, diff --git a/x-pack/test/api_integration/apis/uptime/graphql/monitor_states.ts b/x-pack/test/api_integration/apis/uptime/graphql/monitor_states.ts index a293426195d231..216560583249c5 100644 --- a/x-pack/test/api_integration/apis/uptime/graphql/monitor_states.ts +++ b/x-pack/test/api_integration/apis/uptime/graphql/monitor_states.ts @@ -5,10 +5,11 @@ */ import expect from '@kbn/expect'; -import { monitorStatesQueryString } from '../../../../../legacy/plugins/uptime/public/queries/monitor_states_query'; import { expectFixtureEql } from './helpers/expect_fixture_eql'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { makeChecksWithStatus } from './helpers/make_checks'; +import { monitorStatesQueryString } from '../../../../../legacy/plugins/uptime/public/queries/monitor_states_query'; +import { MonitorSummary } from '../../../../../legacy/plugins/uptime/common/graphql/types'; export default function({ getService }: FtrProviderContext) { const supertest = getService('supertest'); @@ -116,6 +117,7 @@ export default function({ getService }: FtrProviderContext) { } return d; }); + dateRangeEnd = new Date().toISOString(); nonSummaryIp = checks[0][0].monitor.ip; }); @@ -177,5 +179,67 @@ export default function({ getService }: FtrProviderContext) { }); }); }); + + describe(' test status filter', async () => { + const upMonitorId = 'up-test-id'; + const downMonitorId = 'down-test-id'; + const mixMonitorId = 'mix-test-id'; + before('generate three monitors with up, down, mix state', async () => { + await getService('esArchiver').load('uptime/blank'); + + const es = getService('legacyEs'); + + const observer = { + geo: { + name: 'US-East', + location: '40.7128, -74.0060', + }, + }; + + // Generating three monitors each with two geo locations, + // One in a down state , + // One in an up state, + // One in a mix state + + dateRangeStart = new Date().toISOString(); + + await makeChecksWithStatus(es, upMonitorId, 1, 4, 1, {}, 'up'); + await makeChecksWithStatus(es, upMonitorId, 1, 4, 1, { observer }, 'up'); + + await makeChecksWithStatus(es, downMonitorId, 1, 4, 1, {}, 'down'); + await makeChecksWithStatus(es, downMonitorId, 1, 4, 1, { observer }, 'down'); + + await makeChecksWithStatus(es, mixMonitorId, 1, 4, 1, {}, 'up'); + await makeChecksWithStatus(es, mixMonitorId, 1, 4, 1, { observer }, 'down'); + + dateRangeEnd = new Date().toISOString(); + }); + + after('unload heartbeat index', () => getService('esArchiver').unload('uptime/blank')); + + it('should return all monitor when no status filter', async () => { + const { monitorStates } = await getMonitorStates({}); + expect(monitorStates.summaries.length).to.eql(3); + // Summaries are by default sorted by monitor names + expect( + monitorStates.summaries.map((summary: MonitorSummary) => summary.monitor_id) + ).to.eql([downMonitorId, mixMonitorId, upMonitorId]); + }); + + it('should return a monitor with mix state if check status filter is down', async () => { + const { monitorStates } = await getMonitorStates({ statusFilter: 'down' }); + expect(monitorStates.summaries.length).to.eql(2); + monitorStates.summaries.forEach((summary: MonitorSummary) => { + expect(summary.monitor_id).to.not.eql(upMonitorId); + }); + }); + + it('should not return a monitor with mix state if check status filter is up', async () => { + const { monitorStates } = await getMonitorStates({ statusFilter: 'up' }); + + expect(monitorStates.summaries.length).to.eql(1); + expect(monitorStates.summaries[0].monitor_id).to.eql(upMonitorId); + }); + }); }); } diff --git a/x-pack/test/api_integration/apis/uptime/index.js b/x-pack/test/api_integration/apis/uptime/index.ts similarity index 81% rename from x-pack/test/api_integration/apis/uptime/index.js rename to x-pack/test/api_integration/apis/uptime/index.ts index 0b18f14cd7daf2..a21db08d58c4d4 100644 --- a/x-pack/test/api_integration/apis/uptime/index.js +++ b/x-pack/test/api_integration/apis/uptime/index.ts @@ -4,7 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -export default function({ getService, loadTestFile }) { +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function({ getService, loadTestFile }: FtrProviderContext) { const es = getService('legacyEs'); describe('uptime', () => { diff --git a/x-pack/test/api_integration/apis/uptime/rest/index.ts b/x-pack/test/api_integration/apis/uptime/rest/index.ts index f89bec589847ec..9b0cd61c224628 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/index.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/index.ts @@ -42,6 +42,7 @@ export default function({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./snapshot')); loadTestFile(require.resolve('./dynamic_settings')); + loadTestFile(require.resolve('./telemetry_collectors')); }); describe('with real-world data', () => { before('load heartbeat data', async () => await esArchiver.load('uptime/full_heartbeat')); diff --git a/x-pack/test/api_integration/apis/uptime/rest/telemetry_collectors.ts b/x-pack/test/api_integration/apis/uptime/rest/telemetry_collectors.ts new file mode 100644 index 00000000000000..b2ec96be0f2887 --- /dev/null +++ b/x-pack/test/api_integration/apis/uptime/rest/telemetry_collectors.ts @@ -0,0 +1,129 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants'; +import { makeChecksWithStatus } from '../graphql/helpers/make_checks'; + +export default function({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const es = getService('legacyEs'); + + describe('telemetry collectors', () => { + before('generating data', async () => { + await getService('esArchiver').load('uptime/blank'); + + const observer = { + geo: { + name: 'US-East', + location: '40.7128, -74.0060', + }, + }; + + const observer2 = { + geo: { + name: 'US', + location: '40.7128, -74.0060', + }, + }; + + await makeChecksWithStatus( + es, + 'upMonitorId', + 1, + 1, + 60 * 1000, + { + observer: {}, + monitor: { + name: 'Elastic', + }, + }, + 'up' + ); + + await makeChecksWithStatus( + es, + 'downMonitorId', + 1, + 1, + 120 * 1000, + { + observer, + monitor: { + name: 'Long Name with 22 Char', + }, + }, + 'down' + ); + + await makeChecksWithStatus(es, 'noGeoNameMonitor', 1, 1, 60 * 1000, { observer: {} }, 'down'); + await makeChecksWithStatus( + es, + 'downMonitorId', + 1, + 1, + 1, + { + observer, + monitor: { + name: 'Elastic', + }, + }, + 'down' + ); + + await makeChecksWithStatus(es, 'mixMonitorId', 1, 1, 1, { observer: observer2 }, 'down'); + }); + + after('unload heartbeat index', () => getService('esArchiver').unload('uptime/blank')); + + it('should receive expected results after calling monitor/overview logging', async () => { + // call monitor page + await supertest + .post(API_URLS.LOG_PAGE_VIEW) + .set('kbn-xsrf', 'true') + .send({ + page: 'Monitor', + autorefreshInterval: 100, + dateStart: 'now/d', + dateEnd: 'now/d', + autoRefreshEnabled: true, + refreshTelemetryHistory: true, + }) + .expect(200); + + // call overview page + const { body: result } = await supertest + .post(API_URLS.LOG_PAGE_VIEW) + .set('kbn-xsrf', 'true') + .send({ + page: 'Overview', + autorefreshInterval: 60, + dateStart: 'now/d', + dateEnd: 'now-30', + autoRefreshEnabled: true, + }) + .expect(200); + + expect(result).to.eql({ + overview_page: 1, + monitor_page: 1, + no_of_unique_monitors: 4, + settings_page: 0, + monitor_frequency: [120, 0.001, 60, 60], + monitor_name_stats: { min_length: 7, max_length: 22, avg_length: 12 }, + no_of_unique_observer_locations: 3, + observer_location_name_stats: { min_length: 2, max_length: 7, avg_length: 4.8 }, + dateRangeStart: ['now/d', 'now/d'], + dateRangeEnd: ['now/d', 'now-30'], + autoRefreshEnabled: true, + autorefreshInterval: [100, 60], + }); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/uptime/telemetry_collectors.js b/x-pack/test/api_integration/apis/uptime/telemetry_collectors.js deleted file mode 100644 index eb4e3dc0029001..00000000000000 --- a/x-pack/test/api_integration/apis/uptime/telemetry_collectors.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export default function({ getService }) { - const supertest = getService('supertest'); - - describe('telemetry collectors', () => { - it('should receive a 200 for overview page logging', async () => { - await supertest.get('/api/uptime/logOverview').expect(200); - }); - - it('should receive a 200 for monitor page logging', async () => { - await supertest.get('/api/uptime/logMonitor').expect(200); - }); - }); -} diff --git a/x-pack/test/functional/config.edge.js b/x-pack/test/functional/config.edge.js new file mode 100644 index 00000000000000..882fb6fea36861 --- /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/es_archives/fleet/agents/data.json b/x-pack/test/functional/es_archives/fleet/agents/data.json index 9b29767d5162da..1ffb119ca10232 100644 --- a/x-pack/test/functional/es_archives/fleet/agents/data.json +++ b/x-pack/test/functional/es_archives/fleet/agents/data.json @@ -12,30 +12,7 @@ "config_id": "1", "type": "PERMANENT", "local_metadata": "{}", - "user_provided_metadata": "{}", - "actions": [{ - "id": "37ed51ff-e80f-4f2a-a62d-f4fa975e7d85", - "created_at": "2019-09-04T15:04:07+0000", - "type": "RESUME" - }, - { - "id": "b400439c-bbbf-43d5-83cb-cf8b7e32506f", - "type": "PAUSE", - "created_at": "2019-09-04T15:01:07+0000", - "sent_at": "2019-09-04T15:03:07+0000" - }, - { - "created_at" : "2020-03-15T03:47:15.129Z", - "id" : "48cebde1-c906-4893-b89f-595d943b72a1", - "type" : "CONFIG_CHANGE", - "sent_at": "2020-03-04T15:03:07+0000" - }, - { - "created_at" : "2020-03-16T03:47:15.129Z", - "id" : "48cebde1-c906-4893-b89f-595d943b72a2", - "type" : "CONFIG_CHANGE", - "sent_at": "2020-03-04T15:03:07+0000" - }] + "user_provided_metadata": "{}" } } } @@ -54,8 +31,7 @@ "shared_id": "agent2_filebeat", "type": "PERMANENT", "local_metadata": "{}", - "user_provided_metadata": "{}", - "actions": [] + "user_provided_metadata": "{}" } } } @@ -74,8 +50,7 @@ "shared_id": "agent3_metricbeat", "type": "PERMANENT", "local_metadata": "{}", - "user_provided_metadata": "{}", - "actions": [] + "user_provided_metadata": "{}" } } } @@ -94,8 +69,7 @@ "shared_id": "agent4_metricbeat", "type": "PERMANENT", "local_metadata": "{}", - "user_provided_metadata": "{}", - "actions": [] + "user_provided_metadata": "{}" } } } @@ -157,3 +131,71 @@ } } } + +{ + "type": "doc", + "value": { + "id": "agent_actions:37ed51ff-e80f-4f2a-a62d-f4fa975e7d85", + "index": ".kibana", + "source": { + "type": "agent_actions", + "agent_actions": { + "agent_id": "agent1", + "created_at": "2019-09-04T15:04:07+0000", + "type": "RESUME", + "sent_at": "2019-09-04T15:03:07+0000" + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "agent_actions:b400439c-bbbf-43d5-83cb-cf8b7e32506f", + "index": ".kibana", + "source": { + "type": "agent_actions", + "agent_actions": { + "agent_id": "agent1", + "type": "PAUSE", + "created_at": "2019-09-04T15:01:07+0000", + "sent_at": "2019-09-04T15:03:07+0000" + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "agent_actions:48cebde1-c906-4893-b89f-595d943b72a1", + "index": ".kibana", + "source": { + "type": "agent_actions", + "agent_actions": { + "agent_id": "agent1", + "type": "CONFIG_CHANGE", + "created_at": "2020-03-15T03:47:15.129Z", + "sent_at": "2020-03-04T15:03:07+0000" + } + } + } +} + +{ + "type": "doc", + "value": { + "id": "agent_actions:48cebde1-c906-4893-b89f-595d943b72a2", + "index": ".kibana", + "source": { + "type": "agent_actions", + "agent_actions": { + "agent_id": "agent1", + "type": "CONFIG_CHANGE", + "created_at": "2020-03-15T03:47:15.129Z", + "sent_at": "2020-03-04T15:03:07+0000" + } + } + } +} diff --git a/x-pack/test/functional/es_archives/fleet/agents/mappings.json b/x-pack/test/functional/es_archives/fleet/agents/mappings.json index 0f632b7333ee76..31ae1610493035 100644 --- a/x-pack/test/functional/es_archives/fleet/agents/mappings.json +++ b/x-pack/test/functional/es_archives/fleet/agents/mappings.json @@ -9,58 +9,168 @@ "dynamic": "strict", "_meta": { "migrationMappingPropertyHashes": { + "outputs": "aee9782e0d500b867859650a36280165", "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", - "server": "ec97f1c5da1a19609a60874e5af1100c", "visualization": "52d7a13ad68a150c4525b292d23e12cc", "references": "7997cf5a56cc02bdc9c93361bde732b0", "graph-workspace": "cd7ba1330e6682e9cc00b78850874be1", - "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", - "policies": "1a096b98c98c2efebfdba77cefcfe54a", "type": "2f4316de49999235636386fe51dc06c1", - "lens": "21c3ea0763beb1ecb0162529706b88c5", - "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", "infrastructure-ui-source": "ddc0ecb18383f6b26101a2fadb2dab0c", + "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", + "application_usage_totals": "c897e4310c5f24b07caaff3db53ae2c1", + "action": "6e96ac5e648f57523879661ea72525b7", + "agent_configs": "38abaf89513877745c359e7700c0c66a", + "dashboard": "d00f614b29a80360e1190193fd333bab", + "metrics-explorer-view": "53c5365793677328df0ccb6138bf3cdd", + "siem-detection-engine-rule-actions": "90eee2e4635260f4be0a1da8f5bc0aa0", + "agent_events": "3231653fafe4ef3196fe3b32ab774bf2", + "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", + "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", + "application_usage_transactional": "965839e75f809fefe04f92dc4d99722a", + "action_task_params": "a9d49f184ee89641044be0ca2950fa3a", + "apm-indices": "9bb9b2bf1fa636ed8619cbab5ce6a1dd", + "inventory-view": "9ecce5b58867403613d82fe496470b34", + "enrollment_api_keys": "28b91e20b105b6f928e2012600085d8f", + "upgrade-assistant-reindex-operation": "a53a20fe086b72c9a86da3cc12dad8a6", + "cases-comments": "c2061fb929f585df57425102fa928b4b", + "canvas-element": "7390014e1091044523666d97247392fc", + "datasources": "d4bc0c252b2b5683ff21ea32d00acffc", + "telemetry": "36a616f7026dfa617d6655df850fe16d", + "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", + "lens-ui-telemetry": "509bfa5978586998e05f9e303c07a327", + "server": "ec97f1c5da1a19609a60874e5af1100c", + "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", + "lens": "21c3ea0763beb1ecb0162529706b88c5", "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", "search": "181661168bbadd1eff5902361e2a0d5c", "updated_at": "00da57df13e94e9d98437d13ace4bfe0", + "cases-configure": "42711cbb311976c0687853f4c1354572", "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", + "alert": "7b44fba6773e37c806ce290ea9b7024e", + "siem-detection-engine-rule-status": "ae783f41c6937db6b7a2ef5c93a9e9b0", "map": "23d7aa4a720d4938ccde3983f87bd58d", - "dashboard": "d00f614b29a80360e1190193fd333bab", - "apm-services-telemetry": "07ee1939fa4302c62ddc052ec03fed90", - "metrics-explorer-view": "53c5365793677328df0ccb6138bf3cdd", - "epm": "abf5b64aa599932bd181efc86dce14a7", - "siem-ui-timeline": "6485ab095be8d15246667b98a1a34295", - "agent_events": "8060c5567d33f6697164e1fd5c81b8ed", - "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", - "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", + "uptime-dynamic-settings": "b6289473c8985c79b6c47eebc19a0ca5", + "epm-package": "75d12cd13c867fd713d7dfb27366bc20", + "apm-telemetry": "3525d7c22c42bc80f5e6e9cb3f2b26a2", + "cases": "08b8b110dbca273d37e8aef131ecab61", + "siem-ui-timeline": "ac8020190f5950dd3250b6499144e7fb", "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", "url": "c7f66a0df8b1b52f17c28c4adb111105", - "apm-indices": "c69b68f3fe2bf27b4788d4191c1d6011", - "agents": "1c8e942384219bd899f381fd40e407d7", + "agents": "c3eeb7b9d97176f15f6d126370ab23c7", "migrationVersion": "4a1746014a75ade3a714e1db5763276f", - "inventory-view": "84b320fd67209906333ffce261128462", - "enrollment_api_keys": "90e66b79e8e948e9c15434fdb3ae576e", - "upgrade-assistant-reindex-operation": "a53a20fe086b72c9a86da3cc12dad8a6", "index-pattern": "66eccb05066c5a89924f48a9e9736499", - "canvas-element": "7390014e1091044523666d97247392fc", - "datasources": "2fed9e9883b9622cd59a73ee5550ef4f", - "maps-telemetry": "a4229f8b16a6820c6d724b7e0c1f729d", + "maps-telemetry": "268da3a48066123fc5baf35abaa55014", "namespace": "2f4316de49999235636386fe51dc06c1", - "telemetry": "358ffaa88ba34a97d55af0933a117de4", + "cases-user-actions": "32277330ec6b721abe3b846cfd939a71", + "agent_actions": "ed270b46812f0fa1439366c428a2cf17", "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", - "config": "87aca8fdb053154f11383fce3dbf3edf", - "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", - "lens-ui-telemetry": "509bfa5978586998e05f9e303c07a327" + "config": "ae24d22d5986d04124cc6568f771066f", + "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215" } }, "properties": { + "action": { + "properties": { + "actionTypeId": { + "type": "keyword" + }, + "config": { + "type": "object", + "enabled": false + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + } + }, + "secrets": { + "type": "binary" + } + } + }, + "action_task_params": { + "properties": { + "actionId": { + "type": "keyword" + }, + "apiKey": { + "type": "binary" + }, + "params": { + "type": "object", + "enabled": false + } + } + }, + "agent_actions": { + "properties": { + "agent_id": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "data": { + "type": "flattened" + }, + "sent_at": { + "type": "date" + }, + "type": { + "type": "keyword" + } + } + }, + "agent_configs": { + "properties": { + "datasources": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "id": { + "type": "keyword" + }, + "is_default": { + "type": "boolean" + }, + "name": { + "type": "text" + }, + "namespace": { + "type": "keyword" + }, + "revision": { + "type": "integer" + }, + "status": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + }, + "updated_on": { + "type": "keyword" + } + } + }, "agent_events": { "properties": { + "action_id": { + "type": "keyword" + }, "agent_id": { "type": "keyword" }, + "config_id": { + "type": "keyword" + }, "data": { "type": "text" }, @@ -70,6 +180,9 @@ "payload": { "type": "text" }, + "stream_id": { + "type": "keyword" + }, "subtype": { "type": "keyword" }, @@ -86,29 +199,24 @@ "access_api_key_id": { "type": "keyword" }, - "actions": { - "type": "nested", - "properties": { - "created_at": { - "type": "date" - }, - "data": { - "type": "text" - }, - "id": { - "type": "keyword" - }, - "sent_at": { - "type": "date" - }, - "type": { - "type": "keyword" - } - } - }, "active": { "type": "boolean" }, + "config_id": { + "type": "keyword" + }, + "config_newest_revision": { + "type": "integer" + }, + "config_revision": { + "type": "integer" + }, + "current_error_events": { + "type": "text" + }, + "default_api_key": { + "type": "keyword" + }, "enrolled_at": { "type": "date" }, @@ -121,9 +229,6 @@ "local_metadata": { "type": "text" }, - "config_id": { - "type": "keyword" - }, "shared_id": { "type": "keyword" }, @@ -136,21 +241,95 @@ "user_provided_metadata": { "type": "text" }, - "current_error_events": { - "type": "text" - }, "version": { "type": "keyword" } } }, - "apm-indices": { + "alert": { "properties": { - "apm_oss": { + "actions": { + "type": "nested", "properties": { - "apmAgentConfigurationIndex": { + "actionRef": { + "type": "keyword" + }, + "actionTypeId": { + "type": "keyword" + }, + "group": { "type": "keyword" }, + "params": { + "type": "object", + "enabled": false + } + } + }, + "alertTypeId": { + "type": "keyword" + }, + "apiKey": { + "type": "binary" + }, + "apiKeyOwner": { + "type": "keyword" + }, + "consumer": { + "type": "keyword" + }, + "createdAt": { + "type": "date" + }, + "createdBy": { + "type": "keyword" + }, + "enabled": { + "type": "boolean" + }, + "muteAll": { + "type": "boolean" + }, + "mutedInstanceIds": { + "type": "keyword" + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + } + }, + "params": { + "type": "object", + "enabled": false + }, + "schedule": { + "properties": { + "interval": { + "type": "keyword" + } + } + }, + "scheduledTaskId": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "throttle": { + "type": "keyword" + }, + "updatedBy": { + "type": "keyword" + } + } + }, + "apm-indices": { + "properties": { + "apm_oss": { + "properties": { "errorIndices": { "type": "keyword" }, @@ -173,33 +352,779 @@ } } }, - "apm-services-telemetry": { + "apm-telemetry": { "properties": { - "has_any_services": { - "type": "boolean" - }, - "services_per_agent": { + "agents": { "properties": { "dotnet": { - "type": "long", - "null_value": 0 + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "language": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "runtime": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + } + } }, "go": { - "type": "long", - "null_value": 0 + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "language": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "runtime": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + } + } }, "java": { - "type": "long", - "null_value": 0 + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "language": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "runtime": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + } + } }, "js-base": { - "type": "long", - "null_value": 0 - }, - "nodejs": { - "type": "long", - "null_value": 0 - }, + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "language": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "runtime": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + } + } + }, + "nodejs": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "language": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "runtime": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + } + } + }, + "python": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "language": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "runtime": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + } + } + }, + "ruby": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "language": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "runtime": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + } + } + }, + "rum-js": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "language": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + }, + "runtime": { + "properties": { + "composite": { + "type": "keyword", + "ignore_above": 1024 + }, + "name": { + "type": "keyword", + "ignore_above": 1024 + }, + "version": { + "type": "keyword", + "ignore_above": 1024 + } + } + } + } + } + } + } + } + }, + "cardinality": { + "properties": { + "transaction": { + "properties": { + "name": { + "properties": { + "all_agents": { + "properties": { + "1d": { + "type": "long" + } + } + }, + "rum": { + "properties": { + "1d": { + "type": "long" + } + } + } + } + } + } + }, + "user_agent": { + "properties": { + "original": { + "properties": { + "all_agents": { + "properties": { + "1d": { + "type": "long" + } + } + }, + "rum": { + "properties": { + "1d": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "counts": { + "properties": { + "agent_configuration": { + "properties": { + "all": { + "type": "long" + } + } + }, + "error": { + "properties": { + "1d": { + "type": "long" + }, + "all": { + "type": "long" + } + } + }, + "max_error_groups_per_service": { + "properties": { + "1d": { + "type": "long" + } + } + }, + "max_transaction_groups_per_service": { + "properties": { + "1d": { + "type": "long" + } + } + }, + "metric": { + "properties": { + "1d": { + "type": "long" + }, + "all": { + "type": "long" + } + } + }, + "onboarding": { + "properties": { + "1d": { + "type": "long" + }, + "all": { + "type": "long" + } + } + }, + "services": { + "properties": { + "1d": { + "type": "long" + } + } + }, + "sourcemap": { + "properties": { + "1d": { + "type": "long" + }, + "all": { + "type": "long" + } + } + }, + "span": { + "properties": { + "1d": { + "type": "long" + }, + "all": { + "type": "long" + } + } + }, + "traces": { + "properties": { + "1d": { + "type": "long" + } + } + }, + "transaction": { + "properties": { + "1d": { + "type": "long" + }, + "all": { + "type": "long" + } + } + } + } + }, + "has_any_services": { + "type": "boolean" + }, + "indices": { + "properties": { + "all": { + "properties": { + "total": { + "properties": { + "docs": { + "properties": { + "count": { + "type": "long" + } + } + }, + "store": { + "properties": { + "size_in_bytes": { + "type": "long" + } + } + } + } + } + } + }, + "shards": { + "properties": { + "total": { + "type": "long" + } + } + } + } + }, + "integrations": { + "properties": { + "ml": { + "properties": { + "all_jobs_count": { + "type": "long" + } + } + } + } + }, + "retainment": { + "properties": { + "error": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "metric": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "onboarding": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "span": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "transaction": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "services_per_agent": { + "properties": { + "dotnet": { + "type": "long", + "null_value": 0 + }, + "go": { + "type": "long", + "null_value": 0 + }, + "java": { + "type": "long", + "null_value": 0 + }, + "js-base": { + "type": "long", + "null_value": 0 + }, + "nodejs": { + "type": "long", + "null_value": 0 + }, "python": { "type": "long", "null_value": 0 @@ -213,6 +1138,155 @@ "null_value": 0 } } + }, + "tasks": { + "properties": { + "agent_configuration": { + "properties": { + "took": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "agents": { + "properties": { + "took": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "cardinality": { + "properties": { + "took": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "groupings": { + "properties": { + "took": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "indices_stats": { + "properties": { + "took": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "integrations": { + "properties": { + "took": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "processor_events": { + "properties": { + "took": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "services": { + "properties": { + "took": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "versions": { + "properties": { + "took": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + } + } + }, + "version": { + "properties": { + "apm_server": { + "properties": { + "major": { + "type": "long" + }, + "minor": { + "type": "long" + }, + "patch": { + "type": "long" + } + } + } + } + } + } + }, + "application_usage_totals": { + "properties": { + "appId": { + "type": "keyword" + }, + "minutesOnScreen": { + "type": "float" + }, + "numberOfClicks": { + "type": "long" + } + } + }, + "application_usage_transactional": { + "properties": { + "appId": { + "type": "keyword" + }, + "minutesOnScreen": { + "type": "float" + }, + "numberOfClicks": { + "type": "long" + }, + "timestamp": { + "type": "date" } } }, @@ -244,22 +1318,253 @@ } } }, - "canvas-workpad": { - "dynamic": "false", + "canvas-workpad": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + } + } + } + }, + "cases": { + "properties": { + "closed_at": { + "type": "date" + }, + "closed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "description": { + "type": "text" + }, + "external_service": { + "properties": { + "connector_id": { + "type": "keyword" + }, + "connector_name": { + "type": "keyword" + }, + "external_id": { + "type": "keyword" + }, + "external_title": { + "type": "text" + }, + "external_url": { + "type": "text" + }, + "pushed_at": { + "type": "date" + }, + "pushed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "status": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "title": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "cases-comments": { + "properties": { + "comment": { + "type": "text" + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "pushed_at": { + "type": "date" + }, + "pushed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "cases-configure": { + "properties": { + "closure_type": { + "type": "keyword" + }, + "connector_id": { + "type": "keyword" + }, + "connector_name": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "cases-user-actions": { "properties": { - "@created": { - "type": "date" + "action": { + "type": "keyword" }, - "@timestamp": { + "action_at": { "type": "date" }, - "name": { - "type": "text", - "fields": { - "keyword": { + "action_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { "type": "keyword" } } + }, + "action_field": { + "type": "keyword" + }, + "new_value": { + "type": "text" + }, + "old_value": { + "type": "text" } } }, @@ -327,81 +1632,76 @@ }, "datasources": { "properties": { - "id": { - "type": "keyword" - }, - "name": { + "config_id": { "type": "keyword" }, - "package": { - "properties": { - "assets": { - "properties": { - "id": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "description": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "title": { - "type": "keyword" - }, - "version": { - "type": "keyword" - } - } + "description": { + "type": "text" }, - "read_alias": { - "type": "keyword" + "enabled": { + "type": "boolean" }, - "streams": { + "inputs": { + "type": "nested", "properties": { "config": { "type": "flattened" }, - "id": { + "enabled": { + "type": "boolean" + }, + "processors": { "type": "keyword" }, - "input": { + "streams": { + "type": "nested", "properties": { "config": { "type": "flattened" }, - "fields": { - "type": "flattened" - }, - "id": { - "type": "keyword" - }, - "ilm_policy": { + "dataset": { "type": "keyword" }, - "index_template": { - "type": "keyword" + "enabled": { + "type": "boolean" }, - "ingest_pipelines": { + "id": { "type": "keyword" }, - "type": { + "processors": { "type": "keyword" } } }, - "output_id": { + "type": { + "type": "keyword" + } + } + }, + "name": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "output_id": { + "type": "keyword" + }, + "package": { + "properties": { + "name": { + "type": "keyword" + }, + "title": { "type": "keyword" }, - "processors": { + "version": { "type": "keyword" } } + }, + "revision": { + "type": "integer" } } }, @@ -416,49 +1716,18 @@ "api_key_id": { "type": "keyword" }, + "config_id": { + "type": "keyword" + }, "created_at": { "type": "date" }, - "enrollment_rules": { - "type": "nested", - "properties": { - "created_at": { - "type": "date" - }, - "id": { - "type": "keyword" - }, - "ip_ranges": { - "type": "keyword" - }, - "types": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "window_duration": { - "type": "nested", - "properties": { - "from": { - "type": "date" - }, - "to": { - "type": "date" - } - } - } - } - }, "expire_at": { "type": "date" }, "name": { "type": "keyword" }, - "config_id": { - "type": "keyword" - }, "type": { "type": "keyword" }, @@ -467,7 +1736,7 @@ } } }, - "epm": { + "epm-package": { "properties": { "installed": { "type": "nested", @@ -479,6 +1748,12 @@ "type": "keyword" } } + }, + "name": { + "type": "keyword" + }, + "version": { + "type": "keyword" } } }, @@ -631,6 +1906,26 @@ } } }, + "customMetrics": { + "type": "nested", + "properties": { + "aggregation": { + "type": "keyword" + }, + "field": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "label": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, "customOptions": { "type": "nested", "properties": { @@ -665,6 +1960,18 @@ }, "metric": { "properties": { + "aggregation": { + "type": "keyword" + }, + "field": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "label": { + "type": "keyword" + }, "type": { "type": "keyword" } @@ -792,9 +2099,19 @@ } } }, + "indexPatternsWithGeoFieldCount": { + "type": "long" + }, "mapsTotalCount": { "type": "long" }, + "settings": { + "properties": { + "showMapVisualizationTypes": { + "type": "boolean" + } + } + }, "timeCaptured": { "type": "date" } @@ -894,30 +2211,33 @@ "namespace": { "type": "keyword" }, - "policies": { + "outputs": { "properties": { - "datasources": { + "api_key": { "type": "keyword" }, - "description": { - "type": "text" - }, - "id": { + "ca_sha256": { "type": "keyword" }, - "label": { - "type": "keyword" + "config": { + "type": "flattened" }, - "name": { - "type": "text" + "fleet_enroll_password": { + "type": "binary" }, - "status": { + "fleet_enroll_username": { + "type": "binary" + }, + "hosts": { "type": "keyword" }, - "updated_by": { + "is_default": { + "type": "boolean" + }, + "name": { "type": "keyword" }, - "updated_on": { + "type": { "type": "keyword" } } @@ -1011,6 +2331,73 @@ } } }, + "siem-detection-engine-rule-actions": { + "properties": { + "actions": { + "properties": { + "action_type_id": { + "type": "keyword" + }, + "group": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "params": { + "type": "object", + "dynamic": "true" + } + } + }, + "alertThrottle": { + "type": "keyword" + }, + "ruleAlertId": { + "type": "keyword" + }, + "ruleThrottle": { + "type": "keyword" + } + } + }, + "siem-detection-engine-rule-status": { + "properties": { + "alertId": { + "type": "keyword" + }, + "bulkCreateTimeDurations": { + "type": "float" + }, + "gap": { + "type": "text" + }, + "lastFailureAt": { + "type": "date" + }, + "lastFailureMessage": { + "type": "text" + }, + "lastLookBackDate": { + "type": "date" + }, + "lastSuccessAt": { + "type": "date" + }, + "lastSuccessMessage": { + "type": "text" + }, + "searchAfterTimeDurations": { + "type": "float" + }, + "status": { + "type": "keyword" + }, + "statusDate": { + "type": "date" + } + } + }, "siem-ui-timeline": { "properties": { "columns": { @@ -1145,6 +2532,9 @@ "description": { "type": "text" }, + "eventType": { + "type": "keyword" + }, "favorite": { "properties": { "favoriteDate": { @@ -1349,6 +2739,9 @@ }, "telemetry": { "properties": { + "allowChangingOptInStatus": { + "type": "boolean" + }, "enabled": { "type": "boolean" }, @@ -1356,12 +2749,16 @@ "type": "date" }, "lastVersionChecked": { - "type": "keyword", - "ignore_above": 256 + "type": "keyword" + }, + "reportFailureCount": { + "type": "integer" + }, + "reportFailureVersion": { + "type": "keyword" }, "sendUsageFrom": { - "type": "keyword", - "ignore_above": 256 + "type": "keyword" }, "userHasSeenNotice": { "type": "boolean" @@ -1409,6 +2806,13 @@ } } }, + "tsvb-validation-telemetry": { + "properties": { + "failedRequests": { + "type": "long" + } + } + }, "type": { "type": "keyword" }, @@ -1485,6 +2889,13 @@ } } }, + "uptime-dynamic-settings": { + "properties": { + "heartbeatIndices": { + "type": "keyword" + } + } + }, "url": { "properties": { "accessCount": { diff --git a/x-pack/test/functional/page_objects/uptime_page.ts b/x-pack/test/functional/page_objects/uptime_page.ts index fcf2b77dbd624c..39c3c46adddbba 100644 --- a/x-pack/test/functional/page_objects/uptime_page.ts +++ b/x-pack/test/functional/page_objects/uptime_page.ts @@ -105,6 +105,7 @@ export function UptimePageProvider({ getPageObjects, getService }: FtrProviderCo alertTags, alertThrottleInterval, alertTimerangeSelection, + alertType, filters, }: { alertName: string; @@ -113,11 +114,14 @@ export function UptimePageProvider({ getPageObjects, getService }: FtrProviderCo alertThrottleInterval: string; alertNumTimes: string; alertTimerangeSelection: string; + alertType?: string; filters?: string; }) { const { setKueryBarText } = commonService; await alerts.openFlyout(); - await alerts.openMonitorStatusAlertType(); + if (alertType) { + await alerts.openMonitorStatusAlertType(alertType); + } await alerts.setAlertName(alertName); await alerts.setAlertTags(alertTags); await alerts.setAlertInterval(alertInterval); diff --git a/x-pack/test/functional/services/uptime/alerts.ts b/x-pack/test/functional/services/uptime/alerts.ts index 5ee444adec82f9..3a8193ff3d3278 100644 --- a/x-pack/test/functional/services/uptime/alerts.ts +++ b/x-pack/test/functional/services/uptime/alerts.ts @@ -15,8 +15,8 @@ export function UptimeAlertsProvider({ getService }: FtrProviderContext) { await testSubjects.click('xpack.uptime.alertsPopover.toggleButton', 5000); await testSubjects.click('xpack.uptime.toggleAlertFlyout', 5000); }, - async openMonitorStatusAlertType() { - return testSubjects.click('xpack.uptime.alerts.monitorStatus-SelectOption', 5000); + async openMonitorStatusAlertType(alertType: string) { + return testSubjects.click(`xpack.uptime.alerts.${alertType}-SelectOption`, 5000); }, async setAlertTags(tags: string[]) { for (let i = 0; i < tags.length; i += 1) { diff --git a/x-pack/test/plugin_api_integration/config.js b/x-pack/test/plugin_api_integration/config.ts similarity index 77% rename from x-pack/test/plugin_api_integration/config.js rename to x-pack/test/plugin_api_integration/config.ts index 83e8b1f84a9e02..c581e0c246e132 100644 --- a/x-pack/test/plugin_api_integration/config.js +++ b/x-pack/test/plugin_api_integration/config.ts @@ -6,9 +6,10 @@ import path from 'path'; import fs from 'fs'; +import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; import { services } from './services'; -export default async function({ readConfigFile }) { +export default async function({ readConfigFile }: FtrConfigProviderContext) { const integrationConfig = await readConfigFile(require.resolve('../api_integration/config')); // Find all folders in ./plugins since we treat all them as plugin folder @@ -18,7 +19,10 @@ export default async function({ readConfigFile }) { ); return { - testFiles: [require.resolve('./test_suites/task_manager')], + testFiles: [ + require.resolve('./test_suites/task_manager'), + require.resolve('./test_suites/event_log'), + ], services, servers: integrationConfig.get('servers'), esTestCluster: integrationConfig.get('esTestCluster'), @@ -34,6 +38,9 @@ export default async function({ readConfigFile }) { ...integrationConfig.get('kbnTestServer'), serverArgs: [ ...integrationConfig.get('kbnTestServer.serverArgs'), + '--xpack.eventLog.enabled=true', + '--xpack.eventLog.logEntries=true', + '--xpack.eventLog.indexEntries=true', ...plugins.map( pluginDir => `--plugin-path=${path.resolve(__dirname, 'plugins', pluginDir)}` ), diff --git a/x-pack/test/plugin_api_integration/plugins/event_log/kibana.json b/x-pack/test/plugin_api_integration/plugins/event_log/kibana.json new file mode 100644 index 00000000000000..4b467ce9750122 --- /dev/null +++ b/x-pack/test/plugin_api_integration/plugins/event_log/kibana.json @@ -0,0 +1,9 @@ +{ + "id": "event_log_fixture", + "version": "1.0.0", + "kibanaVersion": "kibana", + "configPath": ["xpack"], + "requiredPlugins": ["eventLog"], + "server": true, + "ui": false +} diff --git a/x-pack/test/plugin_api_integration/plugins/event_log/package.json b/x-pack/test/plugin_api_integration/plugins/event_log/package.json new file mode 100644 index 00000000000000..222dfb2338e20c --- /dev/null +++ b/x-pack/test/plugin_api_integration/plugins/event_log/package.json @@ -0,0 +1,15 @@ +{ + "name": "event_log_fixture", + "version": "0.0.0", + "kibana": { + "version": "kibana" + }, + "main": "target/test/plugin_api_integration/plugins/event_log", + "scripts": { + "kbn": "node ../../../../../scripts/kbn.js", + "build": "rm -rf './target' && tsc" + }, + "devDependencies": { + "typescript": "3.7.2" + } +} diff --git a/x-pack/test/plugin_api_integration/plugins/event_log/server/index.ts b/x-pack/test/plugin_api_integration/plugins/event_log/server/index.ts new file mode 100644 index 00000000000000..3d794a7c80fa38 --- /dev/null +++ b/x-pack/test/plugin_api_integration/plugins/event_log/server/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { PluginInitializerContext } from 'kibana/server'; +import { EventLogFixturePlugin } from './plugin'; + +export const plugin = (initContext: PluginInitializerContext) => + new EventLogFixturePlugin(initContext); diff --git a/x-pack/test/plugin_api_integration/plugins/event_log/server/plugin.ts b/x-pack/test/plugin_api_integration/plugins/event_log/server/plugin.ts new file mode 100644 index 00000000000000..eccbd4fb7f90be --- /dev/null +++ b/x-pack/test/plugin_api_integration/plugins/event_log/server/plugin.ts @@ -0,0 +1,98 @@ +/* + * 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 { + Plugin, + CoreSetup, + RequestHandlerContext, + KibanaRequest, + KibanaResponseFactory, + IKibanaResponse, + IRouter, + Logger, + PluginInitializerContext, + RouteValidationResultFactory, +} from 'kibana/server'; +import { + IEventLogService, + IEventLogClientService, + IEventLogger, +} from '../../../../../plugins/event_log/server'; +import { IValidatedEvent } from '../../../../../plugins/event_log/server/types'; + +// this plugin's dependendencies +export interface EventLogFixtureSetupDeps { + eventLog: IEventLogService; +} +export interface EventLogFixtureStartDeps { + eventLog: IEventLogClientService; +} + +export class EventLogFixturePlugin + implements Plugin { + private readonly logger: Logger; + + constructor(initializerContext: PluginInitializerContext) { + this.logger = initializerContext.logger.get('plugins', 'eventLogFixture'); + } + + public setup(core: CoreSetup, { eventLog }: EventLogFixtureSetupDeps) { + const router = core.http.createRouter(); + + eventLog.registerProviderActions('event_log_fixture', ['test']); + const eventLogger = eventLog.getLogger({ + event: { provider: 'event_log_fixture' }, + }); + + core.savedObjects.registerType({ + name: 'event_log_test', + hidden: false, + namespaceAgnostic: true, + mappings: { + properties: {}, + }, + }); + + logEventRoute(router, eventLogger, this.logger); + } + + public start() {} + public stop() {} +} + +const logEventRoute = (router: IRouter, eventLogger: IEventLogger, logger: Logger) => { + router.post( + { + path: `/api/log_event_fixture/{id}/_log`, + validate: { + // removed validation as schema is currently broken in tests + // blocked by: https://github.com/elastic/kibana/issues/61652 + params: (value: any, { ok }: RouteValidationResultFactory) => ok(value), + body: (value: any, { ok }: RouteValidationResultFactory) => ok(value), + }, + }, + async function( + context: RequestHandlerContext, + req: KibanaRequest, + res: KibanaResponseFactory + ): Promise> { + const { id } = req.params as { id: string }; + const event: IValidatedEvent = req.body; + logger.info(`test fixture: log event: ${id} ${JSON.stringify(event)}`); + try { + await context.core.savedObjects.client.get('event_log_test', id); + logger.info(`found existing saved object`); + } catch (ex) { + logger.info(`log event error: ${ex}`); + await context.core.savedObjects.client.create('event_log_test', {}, { id }); + logger.info(`created saved object`); + } + eventLogger.logEvent(event); + logger.info(`logged`); + return res.ok({}); + } + ); +}; diff --git a/x-pack/test/plugin_api_integration/test_suites/event_log/index.ts b/x-pack/test/plugin_api_integration/test_suites/event_log/index.ts new file mode 100644 index 00000000000000..a68378decb1fda --- /dev/null +++ b/x-pack/test/plugin_api_integration/test_suites/event_log/index.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 { FtrProviderContext } from '../../ftr_provider_context'; + +export default function({ loadTestFile }: FtrProviderContext) { + describe('event_log', function taskManagerSuite() { + this.tags('ciGroup2'); + loadTestFile(require.resolve('./public_api_integration')); + loadTestFile(require.resolve('./service_api_integration')); + }); +} diff --git a/x-pack/test/plugin_api_integration/test_suites/event_log/public_api_integration.ts b/x-pack/test/plugin_api_integration/test_suites/event_log/public_api_integration.ts new file mode 100644 index 00000000000000..c440971225d78f --- /dev/null +++ b/x-pack/test/plugin_api_integration/test_suites/event_log/public_api_integration.ts @@ -0,0 +1,236 @@ +/* + * 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 { merge, omit, times, chunk, isEmpty } from 'lodash'; +import uuid from 'uuid'; +import expect from '@kbn/expect/expect.js'; +import moment, { Moment } from 'moment'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { IEvent } from '../../../../plugins/event_log/server'; +import { IValidatedEvent } from '../../../../plugins/event_log/server/types'; + +const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); + +export default function({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const log = getService('log'); + const retry = getService('retry'); + + describe('Event Log public API', () => { + it('should allow querying for events by Saved Object', async () => { + const id = uuid.v4(); + + const expectedEvents = [fakeEvent(id), fakeEvent(id)]; + + await logTestEvent(id, expectedEvents[0]); + await logTestEvent(id, expectedEvents[1]); + + await retry.try(async () => { + const { + body: { data, total }, + } = await findEvents(id, {}); + + expect(data.length).to.be(2); + expect(total).to.be(2); + + assertEventsFromApiMatchCreatedEvents(data, expectedEvents); + }); + }); + + it('should support pagination for events', async () => { + const id = uuid.v4(); + + const timestamp = moment(); + const [firstExpectedEvent, ...expectedEvents] = times(6, () => + fakeEvent(id, fakeEventTiming(timestamp.add(1, 's'))) + ); + // run one first to create the SO and avoid clashes + await logTestEvent(id, firstExpectedEvent); + await Promise.all(expectedEvents.map(event => logTestEvent(id, event))); + + await retry.try(async () => { + const { + body: { data: foundEvents }, + } = await findEvents(id, {}); + + expect(foundEvents.length).to.be(6); + }); + + const [expectedFirstPage, expectedSecondPage] = chunk( + [firstExpectedEvent, ...expectedEvents], + 3 + ); + + const { + body: { data: firstPage }, + } = await findEvents(id, { per_page: 3 }); + + expect(firstPage.length).to.be(3); + assertEventsFromApiMatchCreatedEvents(firstPage, expectedFirstPage); + + const { + body: { data: secondPage }, + } = await findEvents(id, { per_page: 3, page: 2 }); + + expect(secondPage.length).to.be(3); + assertEventsFromApiMatchCreatedEvents(secondPage, expectedSecondPage); + }); + + it('should support sorting by event end', async () => { + const id = uuid.v4(); + + const timestamp = moment(); + const [firstExpectedEvent, ...expectedEvents] = times(6, () => + fakeEvent(id, fakeEventTiming(timestamp.add(1, 's'))) + ); + // run one first to create the SO and avoid clashes + await logTestEvent(id, firstExpectedEvent); + await Promise.all(expectedEvents.map(event => logTestEvent(id, event))); + + await retry.try(async () => { + const { + body: { data: foundEvents }, + } = await findEvents(id, { sort_field: 'event.end', sort_order: 'desc' }); + + expect(foundEvents.length).to.be(6); + assertEventsFromApiMatchCreatedEvents( + foundEvents, + [firstExpectedEvent, ...expectedEvents].reverse() + ); + }); + }); + + it('should support date ranges for events', async () => { + const id = uuid.v4(); + + const timestamp = moment(); + + const firstEvent = fakeEvent(id, fakeEventTiming(timestamp)); + await logTestEvent(id, firstEvent); + await delay(100); + + const start = timestamp.add(1, 's').toISOString(); + + const expectedEvents = times(6, () => fakeEvent(id, fakeEventTiming(timestamp.add(1, 's')))); + await Promise.all(expectedEvents.map(event => logTestEvent(id, event))); + + const end = timestamp.add(1, 's').toISOString(); + + await delay(100); + const lastEvent = fakeEvent(id, fakeEventTiming(timestamp.add(1, 's'))); + await logTestEvent(id, lastEvent); + + await retry.try(async () => { + const { + body: { data: foundEvents, total }, + } = await findEvents(id, {}); + + expect(foundEvents.length).to.be(8); + expect(total).to.be(8); + }); + + const { + body: { data: eventsWithinRange }, + } = await findEvents(id, { start, end }); + + expect(eventsWithinRange.length).to.be(expectedEvents.length); + assertEventsFromApiMatchCreatedEvents(eventsWithinRange, expectedEvents); + + const { + body: { data: eventsFrom }, + } = await findEvents(id, { start }); + + expect(eventsFrom.length).to.be(expectedEvents.length + 1); + assertEventsFromApiMatchCreatedEvents(eventsFrom, [...expectedEvents, lastEvent]); + + const { + body: { data: eventsUntil }, + } = await findEvents(id, { end }); + + expect(eventsUntil.length).to.be(expectedEvents.length + 1); + assertEventsFromApiMatchCreatedEvents(eventsUntil, [firstEvent, ...expectedEvents]); + }); + }); + + async function findEvents(id: string, query: Record = {}) { + const uri = `/api/event_log/event_log_test/${id}/_find${ + isEmpty(query) + ? '' + : `?${Object.entries(query) + .map(([key, val]) => `${key}=${val}`) + .join('&')}` + }`; + log.debug(`calling ${uri}`); + return await supertest + .get(uri) + .set('kbn-xsrf', 'foo') + .expect(200); + } + + function assertEventsFromApiMatchCreatedEvents( + foundEvents: IValidatedEvent[], + expectedEvents: IEvent[] + ) { + try { + foundEvents.forEach((foundEvent: IValidatedEvent, index: number) => { + expect(foundEvent!.event).to.eql(expectedEvents[index]!.event); + expect(omit(foundEvent!.kibana ?? {}, 'server_uuid')).to.eql(expectedEvents[index]!.kibana); + expect(foundEvent!.message).to.eql(expectedEvents[index]!.message); + }); + } catch (ex) { + log.debug(`failed to match ${JSON.stringify({ foundEvents, expectedEvents })}`); + throw ex; + } + } + + async function logTestEvent(id: string, event: IEvent) { + log.debug(`Logging Event for Saved Object ${id}`); + return await supertest + .post(`/api/log_event_fixture/${id}/_log`) + .set('kbn-xsrf', 'foo') + .send(event) + .expect(200); + } + + function fakeEventTiming(start: Moment): Partial { + return { + event: { + start: start.toISOString(), + end: start + .clone() + .add(500, 'milliseconds') + .toISOString(), + }, + }; + } + + function fakeEvent(id: string, overrides: Partial = {}): IEvent { + const start = moment().toISOString(); + const end = moment().toISOString(); + return merge( + { + event: { + provider: 'event_log_fixture', + action: 'test', + start, + end, + duration: 1000000, + }, + kibana: { + namespace: 'default', + saved_objects: [ + { + type: 'event_log_test', + id, + }, + ], + }, + message: `test ${moment().toISOString()}`, + }, + overrides + ); + } +} diff --git a/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts b/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts new file mode 100644 index 00000000000000..b055b22879bf91 --- /dev/null +++ b/x-pack/test/plugin_api_integration/test_suites/event_log/service_api_integration.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export default function() { + describe('Event Log service API', () => { + it('should allow logging an event', async () => {}); + }); +} diff --git a/x-pack/test/siem_cypress/es_archives/prebuilt_rules_loaded/data.json.gz b/x-pack/test/siem_cypress/es_archives/prebuilt_rules_loaded/data.json.gz index 90d4cfe0f27348..573c006d1507d9 100644 Binary files a/x-pack/test/siem_cypress/es_archives/prebuilt_rules_loaded/data.json.gz and b/x-pack/test/siem_cypress/es_archives/prebuilt_rules_loaded/data.json.gz differ diff --git a/x-pack/test/siem_cypress/es_archives/prebuilt_rules_loaded/mappings.json b/x-pack/test/siem_cypress/es_archives/prebuilt_rules_loaded/mappings.json index 99ef5863ba6c86..f4278c4d4318f3 100644 --- a/x-pack/test/siem_cypress/es_archives/prebuilt_rules_loaded/mappings.json +++ b/x-pack/test/siem_cypress/es_archives/prebuilt_rules_loaded/mappings.json @@ -11,12 +11,13 @@ "migrationMappingPropertyHashes": { "action": "6e96ac5e648f57523879661ea72525b7", "action_task_params": "a9d49f184ee89641044be0ca2950fa3a", + "agent_actions": "ed270b46812f0fa1439366c428a2cf17", "agent_configs": "38abaf89513877745c359e7700c0c66a", "agent_events": "3231653fafe4ef3196fe3b32ab774bf2", - "agents": "75c0f4a11560dbc38b65e5e1d98fc9da", + "agents": "c3eeb7b9d97176f15f6d126370ab23c7", "alert": "7b44fba6773e37c806ce290ea9b7024e", "apm-indices": "9bb9b2bf1fa636ed8619cbab5ce6a1dd", - "apm-telemetry": "e8619030e08b671291af04c4603b4944", + "apm-telemetry": "3525d7c22c42bc80f5e6e9cb3f2b26a2", "application_usage_totals": "c897e4310c5f24b07caaff3db53ae2c1", "application_usage_transactional": "965839e75f809fefe04f92dc4d99722a", "canvas-element": "7390014e1091044523666d97247392fc", @@ -29,7 +30,7 @@ "dashboard": "d00f614b29a80360e1190193fd333bab", "datasources": "d4bc0c252b2b5683ff21ea32d00acffc", "enrollment_api_keys": "28b91e20b105b6f928e2012600085d8f", - "epm-package": "75d12cd13c867fd713d7dfb27366bc20", + "epm-package": "0be91c6758421dd5d0f1a58e9e5bc7c3", "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", "graph-workspace": "cd7ba1330e6682e9cc00b78850874be1", "index-pattern": "66eccb05066c5a89924f48a9e9736499", @@ -50,6 +51,7 @@ "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", "search": "181661168bbadd1eff5902361e2a0d5c", "server": "ec97f1c5da1a19609a60874e5af1100c", + "siem-detection-engine-rule-actions": "90eee2e4635260f4be0a1da8f5bc0aa0", "siem-detection-engine-rule-status": "ae783f41c6937db6b7a2ef5c93a9e9b0", "siem-ui-timeline": "ac8020190f5950dd3250b6499144e7fb", "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", @@ -106,6 +108,25 @@ } } }, + "agent_actions": { + "properties": { + "agent_id": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "data": { + "type": "flattened" + }, + "sent_at": { + "type": "date" + }, + "type": { + "type": "keyword" + } + } + }, "agent_configs": { "properties": { "datasources": { @@ -179,26 +200,6 @@ "access_api_key_id": { "type": "keyword" }, - "actions": { - "properties": { - "created_at": { - "type": "date" - }, - "data": { - "type": "text" - }, - "id": { - "type": "keyword" - }, - "sent_at": { - "type": "date" - }, - "type": { - "type": "keyword" - } - }, - "type": "nested" - }, "active": { "type": "boolean" }, @@ -361,7 +362,7 @@ "agent": { "properties": { "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -371,23 +372,31 @@ "framework": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } }, "language": { "properties": { + "composite": { + "ignore_above": 1024, + "type": "keyword" + }, "name": { - "ignore_above": 256, + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, "type": "keyword" } } @@ -395,15 +404,15 @@ "runtime": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -417,7 +426,7 @@ "agent": { "properties": { "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -427,15 +436,15 @@ "framework": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -443,15 +452,15 @@ "language": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -459,15 +468,15 @@ "runtime": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -481,7 +490,7 @@ "agent": { "properties": { "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -491,15 +500,15 @@ "framework": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -507,15 +516,15 @@ "language": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -523,15 +532,15 @@ "runtime": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -545,7 +554,7 @@ "agent": { "properties": { "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -555,15 +564,15 @@ "framework": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -571,15 +580,15 @@ "language": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -587,15 +596,15 @@ "runtime": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -609,7 +618,7 @@ "agent": { "properties": { "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -619,15 +628,15 @@ "framework": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -635,15 +644,15 @@ "language": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -651,15 +660,15 @@ "runtime": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -673,7 +682,7 @@ "agent": { "properties": { "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -683,15 +692,15 @@ "framework": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -699,15 +708,15 @@ "language": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -715,15 +724,15 @@ "runtime": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -737,7 +746,7 @@ "agent": { "properties": { "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -747,15 +756,15 @@ "framework": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -763,15 +772,15 @@ "language": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -779,15 +788,15 @@ "runtime": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -801,7 +810,7 @@ "agent": { "properties": { "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -811,15 +820,15 @@ "framework": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -827,15 +836,15 @@ "language": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -843,15 +852,15 @@ "runtime": { "properties": { "composite": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "name": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" }, "version": { - "ignore_above": 256, + "ignore_above": 1024, "type": "keyword" } } @@ -1565,15 +1574,6 @@ "properties": { "buildNum": { "type": "keyword" - }, - "dateFormat:tz": { - "fields": { - "keyword": { - "ignore_above": 256, - "type": "keyword" - } - }, - "type": "text" } } }, @@ -1750,6 +1750,9 @@ }, "type": "nested" }, + "internal": { + "type": "boolean" + }, "name": { "type": "keyword" }, @@ -2332,6 +2335,36 @@ } } }, + "siem-detection-engine-rule-actions": { + "properties": { + "actions": { + "properties": { + "action_type_id": { + "type": "keyword" + }, + "group": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "params": { + "dynamic": "true", + "type": "object" + } + } + }, + "alertThrottle": { + "type": "keyword" + }, + "ruleAlertId": { + "type": "keyword" + }, + "ruleThrottle": { + "type": "keyword" + } + } + }, "siem-detection-engine-rule-status": { "properties": { "alertId": { @@ -2708,9 +2741,6 @@ } } }, - "spaceId": { - "type": "keyword" - }, "telemetry": { "properties": { "allowChangingOptInStatus": { @@ -2931,2774 +2961,4 @@ } } } -} - -{ - "type": "index", - "value": { - "aliases": { - ".siem-signals-default": { - "is_write_index": true - } - }, - "index": ".siem-signals-default-000001", - "mappings": { - "dynamic": "false", - "properties": { - "@timestamp": { - "type": "date" - }, - "agent": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "client": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "cloud": { - "properties": { - "account": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "availability_zone": { - "ignore_above": 1024, - "type": "keyword" - }, - "instance": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "machine": { - "properties": { - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "region": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "container": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "image": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "tag": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "runtime": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "destination": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "dns": { - "properties": { - "answers": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "data": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "ttl": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "header_flags": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "op_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "question": { - "properties": { - "class": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "subdomain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "resolved_ip": { - "type": "ip" - }, - "response_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ecs": { - "properties": { - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "error": { - "properties": { - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "message": { - "norms": false, - "type": "text" - }, - "stack_trace": { - "doc_values": false, - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "index": false, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "event": { - "properties": { - "action": { - "ignore_above": 1024, - "type": "keyword" - }, - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "code": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "ignore_above": 1024, - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ingested": { - "type": "date" - }, - "kind": { - "ignore_above": 1024, - "type": "keyword" - }, - "module": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "doc_values": false, - "ignore_above": 1024, - "index": false, - "type": "keyword" - }, - "outcome": { - "ignore_above": 1024, - "type": "keyword" - }, - "provider": { - "ignore_above": 1024, - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "file": { - "properties": { - "accessed": { - "type": "date" - }, - "attributes": { - "ignore_above": 1024, - "type": "keyword" - }, - "created": { - "type": "date" - }, - "ctime": { - "type": "date" - }, - "device": { - "ignore_above": 1024, - "type": "keyword" - }, - "directory": { - "ignore_above": 1024, - "type": "keyword" - }, - "drive_letter": { - "ignore_above": 1, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "gid": { - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "inode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mode": { - "ignore_above": 1024, - "type": "keyword" - }, - "mtime": { - "type": "date" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "owner": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "target_path": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uid": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "host": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "http": { - "properties": { - "request": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "method": { - "ignore_above": 1024, - "type": "keyword" - }, - "referrer": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "response": { - "properties": { - "body": { - "properties": { - "bytes": { - "type": "long" - }, - "content": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "bytes": { - "type": "long" - }, - "status_code": { - "type": "long" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "labels": { - "type": "object" - }, - "log": { - "properties": { - "level": { - "ignore_above": 1024, - "type": "keyword" - }, - "logger": { - "ignore_above": 1024, - "type": "keyword" - }, - "origin": { - "properties": { - "file": { - "properties": { - "line": { - "type": "integer" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "function": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "original": { - "doc_values": false, - "ignore_above": 1024, - "index": false, - "type": "keyword" - }, - "syslog": { - "properties": { - "facility": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "priority": { - "type": "long" - }, - "severity": { - "properties": { - "code": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - } - } - }, - "message": { - "norms": false, - "type": "text" - }, - "network": { - "properties": { - "application": { - "ignore_above": 1024, - "type": "keyword" - }, - "bytes": { - "type": "long" - }, - "community_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "direction": { - "ignore_above": 1024, - "type": "keyword" - }, - "forwarded_ip": { - "type": "ip" - }, - "iana_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "packets": { - "type": "long" - }, - "protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "transport": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "observer": { - "properties": { - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hostname": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "product": { - "ignore_above": 1024, - "type": "keyword" - }, - "serial_number": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "vendor": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "organization": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "package": { - "properties": { - "architecture": { - "ignore_above": 1024, - "type": "keyword" - }, - "build_version": { - "ignore_above": 1024, - "type": "keyword" - }, - "checksum": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "install_scope": { - "ignore_above": 1024, - "type": "keyword" - }, - "installed": { - "type": "date" - }, - "license": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "size": { - "type": "long" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "process": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha512": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "parent": { - "properties": { - "args": { - "ignore_above": 1024, - "type": "keyword" - }, - "args_count": { - "type": "long" - }, - "command_line": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "executable": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "exit_code": { - "type": "long" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "pgid": { - "type": "long" - }, - "pid": { - "type": "long" - }, - "ppid": { - "type": "long" - }, - "start": { - "type": "date" - }, - "thread": { - "properties": { - "id": { - "type": "long" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "title": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "uptime": { - "type": "long" - }, - "working_directory": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "registry": { - "properties": { - "data": { - "properties": { - "bytes": { - "ignore_above": 1024, - "type": "keyword" - }, - "strings": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hive": { - "ignore_above": 1024, - "type": "keyword" - }, - "key": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "value": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "related": { - "properties": { - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "ip": { - "type": "ip" - }, - "user": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "ruleset": { - "ignore_above": 1024, - "type": "keyword" - }, - "uuid": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "server": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "service": { - "properties": { - "ephemeral_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "node": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "state": { - "ignore_above": 1024, - "type": "keyword" - }, - "type": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "signal": { - "properties": { - "ancestors": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "original_event": { - "properties": { - "action": { - "type": "keyword" - }, - "category": { - "type": "keyword" - }, - "code": { - "type": "keyword" - }, - "created": { - "type": "date" - }, - "dataset": { - "type": "keyword" - }, - "duration": { - "type": "long" - }, - "end": { - "type": "date" - }, - "hash": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "kind": { - "type": "keyword" - }, - "module": { - "type": "keyword" - }, - "original": { - "doc_values": false, - "index": false, - "type": "keyword" - }, - "outcome": { - "type": "keyword" - }, - "provider": { - "type": "keyword" - }, - "risk_score": { - "type": "float" - }, - "risk_score_norm": { - "type": "float" - }, - "sequence": { - "type": "long" - }, - "severity": { - "type": "long" - }, - "start": { - "type": "date" - }, - "timezone": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "original_time": { - "type": "date" - }, - "parent": { - "properties": { - "depth": { - "type": "long" - }, - "id": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "rule": { - "type": "keyword" - }, - "type": { - "type": "keyword" - } - } - }, - "rule": { - "properties": { - "created_at": { - "type": "date" - }, - "created_by": { - "type": "keyword" - }, - "description": { - "type": "keyword" - }, - "enabled": { - "type": "keyword" - }, - "false_positives": { - "type": "keyword" - }, - "filters": { - "type": "object" - }, - "from": { - "type": "keyword" - }, - "id": { - "type": "keyword" - }, - "immutable": { - "type": "keyword" - }, - "index": { - "type": "keyword" - }, - "interval": { - "type": "keyword" - }, - "language": { - "type": "keyword" - }, - "max_signals": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "note": { - "type": "text" - }, - "output_index": { - "type": "keyword" - }, - "query": { - "type": "keyword" - }, - "references": { - "type": "keyword" - }, - "risk_score": { - "type": "keyword" - }, - "rule_id": { - "type": "keyword" - }, - "saved_id": { - "type": "keyword" - }, - "severity": { - "type": "keyword" - }, - "size": { - "type": "keyword" - }, - "tags": { - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "reference": { - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "type": "keyword" - }, - "name": { - "type": "keyword" - }, - "reference": { - "type": "keyword" - } - } - } - } - }, - "timeline_id": { - "type": "keyword" - }, - "timeline_title": { - "type": "keyword" - }, - "to": { - "type": "keyword" - }, - "type": { - "type": "keyword" - }, - "updated_at": { - "type": "date" - }, - "updated_by": { - "type": "keyword" - }, - "version": { - "type": "keyword" - } - } - }, - "status": { - "type": "keyword" - } - } - }, - "source": { - "properties": { - "address": { - "ignore_above": 1024, - "type": "keyword" - }, - "as": { - "properties": { - "number": { - "type": "long" - }, - "organization": { - "properties": { - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "bytes": { - "type": "long" - }, - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "geo": { - "properties": { - "city_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "continent_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "country_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "location": { - "type": "geo_point" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_iso_code": { - "ignore_above": 1024, - "type": "keyword" - }, - "region_name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "ip": { - "type": "ip" - }, - "mac": { - "ignore_above": 1024, - "type": "keyword" - }, - "nat": { - "properties": { - "ip": { - "type": "ip" - }, - "port": { - "type": "long" - } - } - }, - "packets": { - "type": "long" - }, - "port": { - "type": "long" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "tags": { - "ignore_above": 1024, - "type": "keyword" - }, - "threat": { - "properties": { - "framework": { - "ignore_above": 1024, - "type": "keyword" - }, - "tactic": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "technique": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "tls": { - "properties": { - "cipher": { - "ignore_above": 1024, - "type": "keyword" - }, - "client": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "server_name": { - "ignore_above": 1024, - "type": "keyword" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - }, - "supported_ciphers": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "curve": { - "ignore_above": 1024, - "type": "keyword" - }, - "established": { - "type": "boolean" - }, - "next_protocol": { - "ignore_above": 1024, - "type": "keyword" - }, - "resumed": { - "type": "boolean" - }, - "server": { - "properties": { - "certificate": { - "ignore_above": 1024, - "type": "keyword" - }, - "certificate_chain": { - "ignore_above": 1024, - "type": "keyword" - }, - "hash": { - "properties": { - "md5": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha1": { - "ignore_above": 1024, - "type": "keyword" - }, - "sha256": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "issuer": { - "ignore_above": 1024, - "type": "keyword" - }, - "ja3s": { - "ignore_above": 1024, - "type": "keyword" - }, - "not_after": { - "type": "date" - }, - "not_before": { - "type": "date" - }, - "subject": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - }, - "version_protocol": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "trace": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "transaction": { - "properties": { - "id": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "url": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "extension": { - "ignore_above": 1024, - "type": "keyword" - }, - "fragment": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "password": { - "ignore_above": 1024, - "type": "keyword" - }, - "path": { - "ignore_above": 1024, - "type": "keyword" - }, - "port": { - "type": "long" - }, - "query": { - "ignore_above": 1024, - "type": "keyword" - }, - "registered_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "scheme": { - "ignore_above": 1024, - "type": "keyword" - }, - "top_level_domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "username": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "email": { - "ignore_above": 1024, - "type": "keyword" - }, - "full_name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "group": { - "properties": { - "domain": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "hash": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "user_agent": { - "properties": { - "device": { - "properties": { - "name": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "name": { - "ignore_above": 1024, - "type": "keyword" - }, - "original": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "os": { - "properties": { - "family": { - "ignore_above": 1024, - "type": "keyword" - }, - "full": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "kernel": { - "ignore_above": 1024, - "type": "keyword" - }, - "name": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "platform": { - "ignore_above": 1024, - "type": "keyword" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "vulnerability": { - "properties": { - "category": { - "ignore_above": 1024, - "type": "keyword" - }, - "classification": { - "ignore_above": 1024, - "type": "keyword" - }, - "description": { - "fields": { - "text": { - "norms": false, - "type": "text" - } - }, - "ignore_above": 1024, - "type": "keyword" - }, - "enumeration": { - "ignore_above": 1024, - "type": "keyword" - }, - "id": { - "ignore_above": 1024, - "type": "keyword" - }, - "reference": { - "ignore_above": 1024, - "type": "keyword" - }, - "report_id": { - "ignore_above": 1024, - "type": "keyword" - }, - "scanner": { - "properties": { - "vendor": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "score": { - "properties": { - "base": { - "type": "float" - }, - "environmental": { - "type": "float" - }, - "temporal": { - "type": "float" - }, - "version": { - "ignore_above": 1024, - "type": "keyword" - } - } - }, - "severity": { - "ignore_above": 1024, - "type": "keyword" - } - } - } - } - }, - "settings": { - "index": { - "lifecycle": { - "name": ".siem-signals-default", - "rollover_alias": ".siem-signals-default" - }, - "number_of_replicas": "1", - "number_of_shards": "1" - } - } - } } \ No newline at end of file diff --git a/x-pack/test/siem_cypress/es_archives/timeline_signals/data.json.gz b/x-pack/test/siem_cypress/es_archives/timeline_signals/data.json.gz new file mode 100644 index 00000000000000..485d9868efd21a Binary files /dev/null and b/x-pack/test/siem_cypress/es_archives/timeline_signals/data.json.gz differ diff --git a/x-pack/test/siem_cypress/es_archives/timeline_signals/mappings.json b/x-pack/test/siem_cypress/es_archives/timeline_signals/mappings.json new file mode 100644 index 00000000000000..a1a9e7bfeae7f9 --- /dev/null +++ b/x-pack/test/siem_cypress/es_archives/timeline_signals/mappings.json @@ -0,0 +1,9063 @@ +{ + "type": "index", + "value": { + "aliases": { + ".kibana": { + } + }, + "index": ".kibana_1", + "mappings": { + "_meta": { + "migrationMappingPropertyHashes": { + "action": "c0c235fba02ebd2a2412bcda79009b58", + "action_task_params": "a9d49f184ee89641044be0ca2950fa3a", + "alert": "e588043a01d3d43477e7cad7efa0f5d8", + "apm-indices": "9bb9b2bf1fa636ed8619cbab5ce6a1dd", + "apm-services-telemetry": "07ee1939fa4302c62ddc052ec03fed90", + "canvas-element": "7390014e1091044523666d97247392fc", + "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", + "config": "87aca8fdb053154f11383fce3dbf3edf", + "dashboard": "d00f614b29a80360e1190193fd333bab", + "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", + "graph-workspace": "cd7ba1330e6682e9cc00b78850874be1", + "index-pattern": "66eccb05066c5a89924f48a9e9736499", + "infrastructure-ui-source": "ddc0ecb18383f6b26101a2fadb2dab0c", + "inventory-view": "84b320fd67209906333ffce261128462", + "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", + "lens": "21c3ea0763beb1ecb0162529706b88c5", + "lens-ui-telemetry": "509bfa5978586998e05f9e303c07a327", + "map": "23d7aa4a720d4938ccde3983f87bd58d", + "maps-telemetry": "268da3a48066123fc5baf35abaa55014", + "metrics-explorer-view": "53c5365793677328df0ccb6138bf3cdd", + "migrationVersion": "4a1746014a75ade3a714e1db5763276f", + "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", + "namespace": "2f4316de49999235636386fe51dc06c1", + "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", + "references": "7997cf5a56cc02bdc9c93361bde732b0", + "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", + "search": "181661168bbadd1eff5902361e2a0d5c", + "server": "ec97f1c5da1a19609a60874e5af1100c", + "siem-detection-engine-rule-status": "0367e4d775814b56a4bee29384f9aafe", + "siem-ui-timeline": "ac8020190f5950dd3250b6499144e7fb", + "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", + "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", + "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", + "telemetry": "358ffaa88ba34a97d55af0933a117de4", + "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", + "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", + "type": "2f4316de49999235636386fe51dc06c1", + "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", + "updated_at": "00da57df13e94e9d98437d13ace4bfe0", + "upgrade-assistant-reindex-operation": "a53a20fe086b72c9a86da3cc12dad8a6", + "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", + "url": "c7f66a0df8b1b52f17c28c4adb111105", + "visualization": "52d7a13ad68a150c4525b292d23e12cc" + } + }, + "dynamic": "strict", + "properties": { + "action": { + "properties": { + "actionTypeId": { + "type": "keyword" + }, + "config": { + "enabled": false, + "type": "object" + }, + "name": { + "type": "text" + }, + "secrets": { + "type": "binary" + } + } + }, + "action_task_params": { + "properties": { + "actionId": { + "type": "keyword" + }, + "apiKey": { + "type": "binary" + }, + "params": { + "enabled": false, + "type": "object" + } + } + }, + "alert": { + "properties": { + "actions": { + "properties": { + "actionRef": { + "type": "keyword" + }, + "actionTypeId": { + "type": "keyword" + }, + "group": { + "type": "keyword" + }, + "params": { + "enabled": false, + "type": "object" + } + }, + "type": "nested" + }, + "alertTypeId": { + "type": "keyword" + }, + "apiKey": { + "type": "binary" + }, + "apiKeyOwner": { + "type": "keyword" + }, + "consumer": { + "type": "keyword" + }, + "createdAt": { + "type": "date" + }, + "createdBy": { + "type": "keyword" + }, + "enabled": { + "type": "boolean" + }, + "muteAll": { + "type": "boolean" + }, + "mutedInstanceIds": { + "type": "keyword" + }, + "name": { + "type": "text" + }, + "params": { + "enabled": false, + "type": "object" + }, + "schedule": { + "properties": { + "interval": { + "type": "keyword" + } + } + }, + "scheduledTaskId": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "throttle": { + "type": "keyword" + }, + "updatedBy": { + "type": "keyword" + } + } + }, + "apm-indices": { + "properties": { + "apm_oss": { + "properties": { + "errorIndices": { + "type": "keyword" + }, + "metricsIndices": { + "type": "keyword" + }, + "onboardingIndices": { + "type": "keyword" + }, + "sourcemapIndices": { + "type": "keyword" + }, + "spanIndices": { + "type": "keyword" + }, + "transactionIndices": { + "type": "keyword" + } + } + } + } + }, + "apm-services-telemetry": { + "properties": { + "has_any_services": { + "type": "boolean" + }, + "services_per_agent": { + "properties": { + "dotnet": { + "null_value": 0, + "type": "long" + }, + "go": { + "null_value": 0, + "type": "long" + }, + "java": { + "null_value": 0, + "type": "long" + }, + "js-base": { + "null_value": 0, + "type": "long" + }, + "nodejs": { + "null_value": 0, + "type": "long" + }, + "python": { + "null_value": 0, + "type": "long" + }, + "ruby": { + "null_value": 0, + "type": "long" + }, + "rum-js": { + "null_value": 0, + "type": "long" + } + } + } + } + }, + "canvas-element": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "content": { + "type": "text" + }, + "help": { + "type": "text" + }, + "image": { + "type": "text" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "canvas-workpad": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "config": { + "dynamic": "true", + "properties": { + "buildNum": { + "type": "keyword" + } + } + }, + "dashboard": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "optionsJSON": { + "type": "text" + }, + "panelsJSON": { + "type": "text" + }, + "refreshInterval": { + "properties": { + "display": { + "type": "keyword" + }, + "pause": { + "type": "boolean" + }, + "section": { + "type": "integer" + }, + "value": { + "type": "integer" + } + } + }, + "timeFrom": { + "type": "keyword" + }, + "timeRestore": { + "type": "boolean" + }, + "timeTo": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "file-upload-telemetry": { + "properties": { + "filesUploadedTotalCount": { + "type": "long" + } + } + }, + "graph-workspace": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "numLinks": { + "type": "integer" + }, + "numVertices": { + "type": "integer" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "wsState": { + "type": "text" + } + } + }, + "index-pattern": { + "properties": { + "fieldFormatMap": { + "type": "text" + }, + "fields": { + "type": "text" + }, + "intervalName": { + "type": "keyword" + }, + "notExpandable": { + "type": "boolean" + }, + "sourceFilters": { + "type": "text" + }, + "timeFieldName": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "type": { + "type": "keyword" + }, + "typeMeta": { + "type": "keyword" + } + } + }, + "infrastructure-ui-source": { + "properties": { + "description": { + "type": "text" + }, + "fields": { + "properties": { + "container": { + "type": "keyword" + }, + "host": { + "type": "keyword" + }, + "pod": { + "type": "keyword" + }, + "tiebreaker": { + "type": "keyword" + }, + "timestamp": { + "type": "keyword" + } + } + }, + "logAlias": { + "type": "keyword" + }, + "logColumns": { + "properties": { + "fieldColumn": { + "properties": { + "field": { + "type": "keyword" + }, + "id": { + "type": "keyword" + } + } + }, + "messageColumn": { + "properties": { + "id": { + "type": "keyword" + } + } + }, + "timestampColumn": { + "properties": { + "id": { + "type": "keyword" + } + } + } + }, + "type": "nested" + }, + "metricAlias": { + "type": "keyword" + }, + "name": { + "type": "text" + } + } + }, + "inventory-view": { + "properties": { + "autoBounds": { + "type": "boolean" + }, + "autoReload": { + "type": "boolean" + }, + "boundsOverride": { + "properties": { + "max": { + "type": "integer" + }, + "min": { + "type": "integer" + } + } + }, + "customOptions": { + "properties": { + "field": { + "type": "keyword" + }, + "text": { + "type": "keyword" + } + }, + "type": "nested" + }, + "filterQuery": { + "properties": { + "expression": { + "type": "keyword" + }, + "kind": { + "type": "keyword" + } + } + }, + "groupBy": { + "properties": { + "field": { + "type": "keyword" + }, + "label": { + "type": "keyword" + } + }, + "type": "nested" + }, + "metric": { + "properties": { + "type": { + "type": "keyword" + } + } + }, + "name": { + "type": "keyword" + }, + "nodeType": { + "type": "keyword" + }, + "time": { + "type": "integer" + }, + "view": { + "type": "keyword" + } + } + }, + "kql-telemetry": { + "properties": { + "optInCount": { + "type": "long" + }, + "optOutCount": { + "type": "long" + } + } + }, + "lens": { + "properties": { + "expression": { + "index": false, + "type": "keyword" + }, + "state": { + "type": "flattened" + }, + "title": { + "type": "text" + }, + "visualizationType": { + "type": "keyword" + } + } + }, + "lens-ui-telemetry": { + "properties": { + "count": { + "type": "integer" + }, + "date": { + "type": "date" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "map": { + "properties": { + "bounds": { + "type": "geo_shape" + }, + "description": { + "type": "text" + }, + "layerListJSON": { + "type": "text" + }, + "mapStateJSON": { + "type": "text" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "maps-telemetry": { + "properties": { + "attributesPerMap": { + "properties": { + "dataSourcesCount": { + "properties": { + "avg": { + "type": "long" + }, + "max": { + "type": "long" + }, + "min": { + "type": "long" + } + } + }, + "emsVectorLayersCount": { + "dynamic": "true", + "type": "object" + }, + "layerTypesCount": { + "dynamic": "true", + "type": "object" + }, + "layersCount": { + "properties": { + "avg": { + "type": "long" + }, + "max": { + "type": "long" + }, + "min": { + "type": "long" + } + } + } + } + }, + "indexPatternsWithGeoFieldCount": { + "type": "long" + }, + "mapsTotalCount": { + "type": "long" + }, + "settings": { + "properties": { + "showMapVisualizationTypes": { + "type": "boolean" + } + } + }, + "timeCaptured": { + "type": "date" + } + } + }, + "metrics-explorer-view": { + "properties": { + "chartOptions": { + "properties": { + "stack": { + "type": "boolean" + }, + "type": { + "type": "keyword" + }, + "yAxisMode": { + "type": "keyword" + } + } + }, + "currentTimerange": { + "properties": { + "from": { + "type": "keyword" + }, + "interval": { + "type": "keyword" + }, + "to": { + "type": "keyword" + } + } + }, + "name": { + "type": "keyword" + }, + "options": { + "properties": { + "aggregation": { + "type": "keyword" + }, + "filterQuery": { + "type": "keyword" + }, + "groupBy": { + "type": "keyword" + }, + "limit": { + "type": "integer" + }, + "metrics": { + "properties": { + "aggregation": { + "type": "keyword" + }, + "color": { + "type": "keyword" + }, + "field": { + "type": "keyword" + }, + "label": { + "type": "keyword" + } + }, + "type": "nested" + } + } + } + } + }, + "migrationVersion": { + "dynamic": "true", + "properties": { + "dashboard": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "index-pattern": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "search": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "space": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "visualization": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "ml-telemetry": { + "properties": { + "file_data_visualizer": { + "properties": { + "index_creation_count": { + "type": "long" + } + } + } + } + }, + "namespace": { + "type": "keyword" + }, + "query": { + "properties": { + "description": { + "type": "text" + }, + "filters": { + "enabled": false, + "type": "object" + }, + "query": { + "properties": { + "language": { + "type": "keyword" + }, + "query": { + "index": false, + "type": "keyword" + } + } + }, + "timefilter": { + "enabled": false, + "type": "object" + }, + "title": { + "type": "text" + } + } + }, + "references": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "sample-data-telemetry": { + "properties": { + "installCount": { + "type": "long" + }, + "unInstallCount": { + "type": "long" + } + } + }, + "search": { + "properties": { + "columns": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "sort": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "server": { + "properties": { + "uuid": { + "type": "keyword" + } + } + }, + "siem-detection-engine-rule-status": { + "properties": { + "alertId": { + "type": "keyword" + }, + "lastFailureAt": { + "type": "date" + }, + "lastFailureMessage": { + "type": "text" + }, + "lastSuccessAt": { + "type": "date" + }, + "lastSuccessMessage": { + "type": "text" + }, + "status": { + "type": "keyword" + }, + "statusDate": { + "type": "date" + } + } + }, + "siem-ui-timeline": { + "properties": { + "columns": { + "properties": { + "aggregatable": { + "type": "boolean" + }, + "category": { + "type": "keyword" + }, + "columnHeaderType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "example": { + "type": "text" + }, + "id": { + "type": "keyword" + }, + "indexes": { + "type": "keyword" + }, + "name": { + "type": "text" + }, + "placeholder": { + "type": "text" + }, + "searchable": { + "type": "boolean" + }, + "type": { + "type": "keyword" + } + } + }, + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "dataProviders": { + "properties": { + "and": { + "properties": { + "enabled": { + "type": "boolean" + }, + "excluded": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "kqlQuery": { + "type": "text" + }, + "name": { + "type": "text" + }, + "queryMatch": { + "properties": { + "displayField": { + "type": "text" + }, + "displayValue": { + "type": "text" + }, + "field": { + "type": "text" + }, + "operator": { + "type": "text" + }, + "value": { + "type": "text" + } + } + } + } + }, + "enabled": { + "type": "boolean" + }, + "excluded": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "kqlQuery": { + "type": "text" + }, + "name": { + "type": "text" + }, + "queryMatch": { + "properties": { + "displayField": { + "type": "text" + }, + "displayValue": { + "type": "text" + }, + "field": { + "type": "text" + }, + "operator": { + "type": "text" + }, + "value": { + "type": "text" + } + } + } + } + }, + "dateRange": { + "properties": { + "end": { + "type": "date" + }, + "start": { + "type": "date" + } + } + }, + "description": { + "type": "text" + }, + "eventType": { + "type": "keyword" + }, + "favorite": { + "properties": { + "favoriteDate": { + "type": "date" + }, + "fullName": { + "type": "text" + }, + "keySearch": { + "type": "text" + }, + "userName": { + "type": "text" + } + } + }, + "filters": { + "properties": { + "exists": { + "type": "text" + }, + "match_all": { + "type": "text" + }, + "meta": { + "properties": { + "alias": { + "type": "text" + }, + "controlledBy": { + "type": "text" + }, + "disabled": { + "type": "boolean" + }, + "field": { + "type": "text" + }, + "formattedValue": { + "type": "text" + }, + "index": { + "type": "keyword" + }, + "key": { + "type": "keyword" + }, + "negate": { + "type": "boolean" + }, + "params": { + "type": "text" + }, + "type": { + "type": "keyword" + }, + "value": { + "type": "text" + } + } + }, + "missing": { + "type": "text" + }, + "query": { + "type": "text" + }, + "range": { + "type": "text" + }, + "script": { + "type": "text" + } + } + }, + "kqlMode": { + "type": "keyword" + }, + "kqlQuery": { + "properties": { + "filterQuery": { + "properties": { + "kuery": { + "properties": { + "expression": { + "type": "text" + }, + "kind": { + "type": "keyword" + } + } + }, + "serializedQuery": { + "type": "text" + } + } + } + } + }, + "savedQueryId": { + "type": "keyword" + }, + "sort": { + "properties": { + "columnId": { + "type": "keyword" + }, + "sortDirection": { + "type": "keyword" + } + } + }, + "title": { + "type": "text" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "siem-ui-timeline-note": { + "properties": { + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "eventId": { + "type": "keyword" + }, + "note": { + "type": "text" + }, + "timelineId": { + "type": "keyword" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "siem-ui-timeline-pinned-event": { + "properties": { + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "eventId": { + "type": "keyword" + }, + "timelineId": { + "type": "keyword" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "space": { + "properties": { + "_reserved": { + "type": "boolean" + }, + "color": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "disabledFeatures": { + "type": "keyword" + }, + "imageUrl": { + "index": false, + "type": "text" + }, + "initials": { + "type": "keyword" + }, + "name": { + "fields": { + "keyword": { + "ignore_above": 2048, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "telemetry": { + "properties": { + "enabled": { + "type": "boolean" + }, + "lastReported": { + "type": "date" + }, + "lastVersionChecked": { + "ignore_above": 256, + "type": "keyword" + }, + "sendUsageFrom": { + "ignore_above": 256, + "type": "keyword" + }, + "userHasSeenNotice": { + "type": "boolean" + } + } + }, + "timelion-sheet": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "timelion_chart_height": { + "type": "integer" + }, + "timelion_columns": { + "type": "integer" + }, + "timelion_interval": { + "type": "keyword" + }, + "timelion_other_interval": { + "type": "keyword" + }, + "timelion_rows": { + "type": "integer" + }, + "timelion_sheet": { + "type": "text" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "tsvb-validation-telemetry": { + "properties": { + "failedRequests": { + "type": "long" + } + } + }, + "type": { + "type": "keyword" + }, + "ui-metric": { + "properties": { + "count": { + "type": "integer" + } + } + }, + "updated_at": { + "type": "date" + }, + "upgrade-assistant-reindex-operation": { + "dynamic": "true", + "properties": { + "indexName": { + "type": "keyword" + }, + "status": { + "type": "integer" + } + } + }, + "upgrade-assistant-telemetry": { + "properties": { + "features": { + "properties": { + "deprecation_logging": { + "properties": { + "enabled": { + "null_value": true, + "type": "boolean" + } + } + } + } + }, + "ui_open": { + "properties": { + "cluster": { + "null_value": 0, + "type": "long" + }, + "indices": { + "null_value": 0, + "type": "long" + }, + "overview": { + "null_value": 0, + "type": "long" + } + } + }, + "ui_reindex": { + "properties": { + "close": { + "null_value": 0, + "type": "long" + }, + "open": { + "null_value": 0, + "type": "long" + }, + "start": { + "null_value": 0, + "type": "long" + }, + "stop": { + "null_value": 0, + "type": "long" + } + } + } + } + }, + "url": { + "properties": { + "accessCount": { + "type": "long" + }, + "accessDate": { + "type": "date" + }, + "createDate": { + "type": "date" + }, + "url": { + "fields": { + "keyword": { + "ignore_above": 2048, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "visualization": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "savedSearchRefName": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "visState": { + "type": "text" + } + } + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "number_of_replicas": "1", + "number_of_shards": "1" + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + ".siem-signals-default": { + "is_write_index": true + } + }, + "index": ".siem-signals-default-000001", + "mappings": { + "dynamic": "false", + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "client": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "cloud": { + "properties": { + "account": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "instance": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "machine": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "container": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "image": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "tag": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "type": "object" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "runtime": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "dns": { + "properties": { + "answers": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "ttl": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "header_flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "op_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "question": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "resolved_ip": { + "type": "ip" + }, + "response_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ecs": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "error": { + "properties": { + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "message": { + "norms": false, + "type": "text" + }, + "stack_trace": { + "doc_values": false, + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "index": false, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "event": { + "properties": { + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "ignore_above": 1024, + "type": "keyword" + }, + "duration": { + "type": "long" + }, + "end": { + "type": "date" + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingested": { + "type": "date" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "doc_values": false, + "ignore_above": 1024, + "index": false, + "type": "keyword" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "directory": { + "ignore_above": 1024, + "type": "keyword" + }, + "drive_letter": { + "ignore_above": 1, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mtime": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "target_path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "http": { + "properties": { + "request": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "method": { + "ignore_above": 1024, + "type": "keyword" + }, + "referrer": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "response": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "status_code": { + "type": "long" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "type": "object" + }, + "log": { + "properties": { + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "logger": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "properties": { + "file": { + "properties": { + "line": { + "type": "integer" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "function": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "original": { + "doc_values": false, + "ignore_above": 1024, + "index": false, + "type": "keyword" + }, + "syslog": { + "properties": { + "facility": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "priority": { + "type": "long" + }, + "severity": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "message": { + "norms": false, + "type": "text" + }, + "network": { + "properties": { + "application": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "type": "long" + }, + "community_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "forwarded_ip": { + "type": "ip" + }, + "iana_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "packets": { + "type": "long" + }, + "protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "transport": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "observer": { + "properties": { + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "vendor": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "organization": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "package": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "build_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "checksum": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "install_scope": { + "ignore_above": 1024, + "type": "keyword" + }, + "installed": { + "type": "date" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "process": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "strings": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hive": { + "ignore_above": 1024, + "type": "keyword" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "related": { + "properties": { + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "rule": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "ruleset": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "server": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "service": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "signal": { + "properties": { + "ancestors": { + "properties": { + "depth": { + "type": "long" + }, + "id": { + "type": "keyword" + }, + "rule": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "original_event": { + "properties": { + "action": { + "type": "keyword" + }, + "category": { + "type": "keyword" + }, + "code": { + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "type": "keyword" + }, + "duration": { + "type": "long" + }, + "end": { + "type": "date" + }, + "hash": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "kind": { + "type": "keyword" + }, + "module": { + "type": "keyword" + }, + "original": { + "doc_values": false, + "index": false, + "type": "keyword" + }, + "outcome": { + "type": "keyword" + }, + "provider": { + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "original_time": { + "type": "date" + }, + "parent": { + "properties": { + "depth": { + "type": "long" + }, + "id": { + "type": "keyword" + }, + "index": { + "type": "keyword" + }, + "rule": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "rule": { + "properties": { + "created_at": { + "type": "date" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "keyword" + }, + "enabled": { + "type": "keyword" + }, + "false_positives": { + "type": "keyword" + }, + "filters": { + "type": "object" + }, + "from": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "immutable": { + "type": "keyword" + }, + "index": { + "type": "keyword" + }, + "interval": { + "type": "keyword" + }, + "language": { + "type": "keyword" + }, + "max_signals": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "output_index": { + "type": "keyword" + }, + "query": { + "type": "keyword" + }, + "references": { + "type": "keyword" + }, + "risk_score": { + "type": "keyword" + }, + "rule_id": { + "type": "keyword" + }, + "saved_id": { + "type": "keyword" + }, + "severity": { + "type": "keyword" + }, + "size": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "threat": { + "properties": { + "framework": { + "type": "keyword" + }, + "tactic": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "reference": { + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "reference": { + "type": "keyword" + } + } + } + } + }, + "timeline_id": { + "type": "keyword" + }, + "timeline_title": { + "type": "keyword" + }, + "to": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "status": { + "type": "keyword" + } + } + }, + "source": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "tags": { + "ignore_above": 1024, + "type": "keyword" + }, + "threat": { + "properties": { + "framework": { + "ignore_above": 1024, + "type": "keyword" + }, + "tactic": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "tls": { + "properties": { + "cipher": { + "ignore_above": 1024, + "type": "keyword" + }, + "client": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "server_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + }, + "supported_ciphers": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "established": { + "type": "boolean" + }, + "next_protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "resumed": { + "type": "boolean" + }, + "server": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3s": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + }, + "version_protocol": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "trace": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "transaction": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "url": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fragment": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "password": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "query": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "scheme": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "username": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user_agent": { + "properties": { + "device": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vulnerability": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "classification": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "enumeration": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "report_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "scanner": { + "properties": { + "vendor": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "score": { + "properties": { + "base": { + "type": "float" + }, + "environmental": { + "type": "float" + }, + "temporal": { + "type": "float" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "severity": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "lifecycle": { + "name": ".siem-signals-default", + "rollover_alias": ".siem-signals-default" + }, + "number_of_replicas": "1", + "number_of_shards": "1" + } + } + } +} + +{ + "type": "index", + "value": { + "aliases": { + "auditbeat-7.6.2": { + "is_write_index": true + } + }, + "index": "auditbeat-7.6.2-2020.03.20-000001", + "mappings": { + "_meta": { + "beat": "auditbeat", + "version": "7.6.2" + }, + "date_detection": false, + "dynamic_templates": [ + { + "labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "labels.*" + } + }, + { + "container.labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "container.labels.*" + } + }, + { + "dns.answers": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "dns.answers.*" + } + }, + { + "log.syslog": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "log.syslog.*" + } + }, + { + "fields": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "fields.*" + } + }, + { + "docker.container.labels": { + "mapping": { + "type": "keyword" + }, + "match_mapping_type": "string", + "path_match": "docker.container.labels.*" + } + }, + { + "kubernetes.labels.*": { + "mapping": { + "type": "keyword" + }, + "path_match": "kubernetes.labels.*" + } + }, + { + "kubernetes.annotations.*": { + "mapping": { + "type": "keyword" + }, + "path_match": "kubernetes.annotations.*" + } + }, + { + "strings_as_keyword": { + "mapping": { + "ignore_above": 1024, + "type": "keyword" + }, + "match_mapping_type": "string" + } + } + ], + "properties": { + "@timestamp": { + "type": "date" + }, + "agent": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "auditd": { + "properties": { + "data": { + "properties": { + "a0": { + "ignore_above": 1024, + "type": "keyword" + }, + "a1": { + "ignore_above": 1024, + "type": "keyword" + }, + "a2": { + "ignore_above": 1024, + "type": "keyword" + }, + "a3": { + "ignore_above": 1024, + "type": "keyword" + }, + "a[0-3]": { + "ignore_above": 1024, + "type": "keyword" + }, + "acct": { + "ignore_above": 1024, + "type": "keyword" + }, + "acl": { + "ignore_above": 1024, + "type": "keyword" + }, + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "added": { + "ignore_above": 1024, + "type": "keyword" + }, + "addr": { + "ignore_above": 1024, + "type": "keyword" + }, + "apparmor": { + "ignore_above": 1024, + "type": "keyword" + }, + "arch": { + "ignore_above": 1024, + "type": "keyword" + }, + "argc": { + "ignore_above": 1024, + "type": "keyword" + }, + "audit_backlog_limit": { + "ignore_above": 1024, + "type": "keyword" + }, + "audit_backlog_wait_time": { + "ignore_above": 1024, + "type": "keyword" + }, + "audit_enabled": { + "ignore_above": 1024, + "type": "keyword" + }, + "audit_failure": { + "ignore_above": 1024, + "type": "keyword" + }, + "banners": { + "ignore_above": 1024, + "type": "keyword" + }, + "bool": { + "ignore_above": 1024, + "type": "keyword" + }, + "bus": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fe": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fi": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fp": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_fver": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_pe": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_pi": { + "ignore_above": 1024, + "type": "keyword" + }, + "cap_pp": { + "ignore_above": 1024, + "type": "keyword" + }, + "capability": { + "ignore_above": 1024, + "type": "keyword" + }, + "cgroup": { + "ignore_above": 1024, + "type": "keyword" + }, + "changed": { + "ignore_above": 1024, + "type": "keyword" + }, + "cipher": { + "ignore_above": 1024, + "type": "keyword" + }, + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "cmd": { + "ignore_above": 1024, + "type": "keyword" + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "compat": { + "ignore_above": 1024, + "type": "keyword" + }, + "daddr": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "default-context": { + "ignore_above": 1024, + "type": "keyword" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "dir": { + "ignore_above": 1024, + "type": "keyword" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "dmac": { + "ignore_above": 1024, + "type": "keyword" + }, + "dport": { + "ignore_above": 1024, + "type": "keyword" + }, + "enforcing": { + "ignore_above": 1024, + "type": "keyword" + }, + "entries": { + "ignore_above": 1024, + "type": "keyword" + }, + "exit": { + "ignore_above": 1024, + "type": "keyword" + }, + "fam": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "fd": { + "ignore_above": 1024, + "type": "keyword" + }, + "fe": { + "ignore_above": 1024, + "type": "keyword" + }, + "feature": { + "ignore_above": 1024, + "type": "keyword" + }, + "fi": { + "ignore_above": 1024, + "type": "keyword" + }, + "file": { + "ignore_above": 1024, + "type": "keyword" + }, + "flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "format": { + "ignore_above": 1024, + "type": "keyword" + }, + "fp": { + "ignore_above": 1024, + "type": "keyword" + }, + "fver": { + "ignore_above": 1024, + "type": "keyword" + }, + "grantors": { + "ignore_above": 1024, + "type": "keyword" + }, + "grp": { + "ignore_above": 1024, + "type": "keyword" + }, + "hook": { + "ignore_above": 1024, + "type": "keyword" + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "icmp_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "igid": { + "ignore_above": 1024, + "type": "keyword" + }, + "img-ctx": { + "ignore_above": 1024, + "type": "keyword" + }, + "inif": { + "ignore_above": 1024, + "type": "keyword" + }, + "ino": { + "ignore_above": 1024, + "type": "keyword" + }, + "inode_gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "inode_uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "invalid_context": { + "ignore_above": 1024, + "type": "keyword" + }, + "ioctlcmd": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "ignore_above": 1024, + "type": "keyword" + }, + "ipid": { + "ignore_above": 1024, + "type": "keyword" + }, + "ipx-net": { + "ignore_above": 1024, + "type": "keyword" + }, + "items": { + "ignore_above": 1024, + "type": "keyword" + }, + "iuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "ksize": { + "ignore_above": 1024, + "type": "keyword" + }, + "laddr": { + "ignore_above": 1024, + "type": "keyword" + }, + "len": { + "ignore_above": 1024, + "type": "keyword" + }, + "list": { + "ignore_above": 1024, + "type": "keyword" + }, + "lport": { + "ignore_above": 1024, + "type": "keyword" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "macproto": { + "ignore_above": 1024, + "type": "keyword" + }, + "maj": { + "ignore_above": 1024, + "type": "keyword" + }, + "major": { + "ignore_above": 1024, + "type": "keyword" + }, + "minor": { + "ignore_above": 1024, + "type": "keyword" + }, + "model": { + "ignore_above": 1024, + "type": "keyword" + }, + "msg": { + "ignore_above": 1024, + "type": "keyword" + }, + "nargs": { + "ignore_above": 1024, + "type": "keyword" + }, + "net": { + "ignore_above": 1024, + "type": "keyword" + }, + "new": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-chardev": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-disk": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-enabled": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-fs": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-level": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-log_passwd": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-mem": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-net": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-range": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-rng": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-role": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-seuser": { + "ignore_above": 1024, + "type": "keyword" + }, + "new-vcpu": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_lock": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_pe": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_pi": { + "ignore_above": 1024, + "type": "keyword" + }, + "new_pp": { + "ignore_above": 1024, + "type": "keyword" + }, + "nlnk-fam": { + "ignore_above": 1024, + "type": "keyword" + }, + "nlnk-grp": { + "ignore_above": 1024, + "type": "keyword" + }, + "nlnk-pid": { + "ignore_above": 1024, + "type": "keyword" + }, + "oauid": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "ocomm": { + "ignore_above": 1024, + "type": "keyword" + }, + "oflag": { + "ignore_above": 1024, + "type": "keyword" + }, + "old": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-auid": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-chardev": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-disk": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-enabled": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-fs": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-level": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-log_passwd": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-mem": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-net": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-range": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-rng": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-role": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-ses": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-seuser": { + "ignore_above": 1024, + "type": "keyword" + }, + "old-vcpu": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_enforcing": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_lock": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_pe": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_pi": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_pp": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_prom": { + "ignore_above": 1024, + "type": "keyword" + }, + "old_val": { + "ignore_above": 1024, + "type": "keyword" + }, + "op": { + "ignore_above": 1024, + "type": "keyword" + }, + "opid": { + "ignore_above": 1024, + "type": "keyword" + }, + "oses": { + "ignore_above": 1024, + "type": "keyword" + }, + "outif": { + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "ignore_above": 1024, + "type": "keyword" + }, + "per": { + "ignore_above": 1024, + "type": "keyword" + }, + "perm": { + "ignore_above": 1024, + "type": "keyword" + }, + "perm_mask": { + "ignore_above": 1024, + "type": "keyword" + }, + "permissive": { + "ignore_above": 1024, + "type": "keyword" + }, + "pfs": { + "ignore_above": 1024, + "type": "keyword" + }, + "printer": { + "ignore_above": 1024, + "type": "keyword" + }, + "prom": { + "ignore_above": 1024, + "type": "keyword" + }, + "proto": { + "ignore_above": 1024, + "type": "keyword" + }, + "qbytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "range": { + "ignore_above": 1024, + "type": "keyword" + }, + "reason": { + "ignore_above": 1024, + "type": "keyword" + }, + "removed": { + "ignore_above": 1024, + "type": "keyword" + }, + "res": { + "ignore_above": 1024, + "type": "keyword" + }, + "resrc": { + "ignore_above": 1024, + "type": "keyword" + }, + "rport": { + "ignore_above": 1024, + "type": "keyword" + }, + "sauid": { + "ignore_above": 1024, + "type": "keyword" + }, + "scontext": { + "ignore_above": 1024, + "type": "keyword" + }, + "selected-context": { + "ignore_above": 1024, + "type": "keyword" + }, + "seperm": { + "ignore_above": 1024, + "type": "keyword" + }, + "seperms": { + "ignore_above": 1024, + "type": "keyword" + }, + "seqno": { + "ignore_above": 1024, + "type": "keyword" + }, + "seresult": { + "ignore_above": 1024, + "type": "keyword" + }, + "ses": { + "ignore_above": 1024, + "type": "keyword" + }, + "seuser": { + "ignore_above": 1024, + "type": "keyword" + }, + "sig": { + "ignore_above": 1024, + "type": "keyword" + }, + "sigev_signo": { + "ignore_above": 1024, + "type": "keyword" + }, + "smac": { + "ignore_above": 1024, + "type": "keyword" + }, + "socket": { + "properties": { + "addr": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "ignore_above": 1024, + "type": "keyword" + }, + "saddr": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "spid": { + "ignore_above": 1024, + "type": "keyword" + }, + "sport": { + "ignore_above": 1024, + "type": "keyword" + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "subj": { + "ignore_above": 1024, + "type": "keyword" + }, + "success": { + "ignore_above": 1024, + "type": "keyword" + }, + "syscall": { + "ignore_above": 1024, + "type": "keyword" + }, + "table": { + "ignore_above": 1024, + "type": "keyword" + }, + "tclass": { + "ignore_above": 1024, + "type": "keyword" + }, + "tcontext": { + "ignore_above": 1024, + "type": "keyword" + }, + "terminal": { + "ignore_above": 1024, + "type": "keyword" + }, + "tty": { + "ignore_above": 1024, + "type": "keyword" + }, + "unit": { + "ignore_above": 1024, + "type": "keyword" + }, + "uri": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "val": { + "ignore_above": 1024, + "type": "keyword" + }, + "ver": { + "ignore_above": 1024, + "type": "keyword" + }, + "virt": { + "ignore_above": 1024, + "type": "keyword" + }, + "vm": { + "ignore_above": 1024, + "type": "keyword" + }, + "vm-ctx": { + "ignore_above": 1024, + "type": "keyword" + }, + "vm-pid": { + "ignore_above": 1024, + "type": "keyword" + }, + "watch": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "message_type": { + "ignore_above": 1024, + "type": "keyword" + }, + "paths": { + "properties": { + "dev": { + "ignore_above": 1024, + "type": "keyword" + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "item": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "nametype": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_level": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_role": { + "ignore_above": 1024, + "type": "keyword" + }, + "obj_user": { + "ignore_above": 1024, + "type": "keyword" + }, + "objtype": { + "ignore_above": 1024, + "type": "keyword" + }, + "ogid": { + "ignore_above": 1024, + "type": "keyword" + }, + "ouid": { + "ignore_above": 1024, + "type": "keyword" + }, + "rdev": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "result": { + "ignore_above": 1024, + "type": "keyword" + }, + "sequence": { + "type": "long" + }, + "session": { + "ignore_above": 1024, + "type": "keyword" + }, + "summary": { + "properties": { + "actor": { + "properties": { + "primary": { + "ignore_above": 1024, + "type": "keyword" + }, + "secondary": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "how": { + "ignore_above": 1024, + "type": "keyword" + }, + "object": { + "properties": { + "primary": { + "ignore_above": 1024, + "type": "keyword" + }, + "secondary": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "client": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "cloud": { + "properties": { + "account": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "availability_zone": { + "ignore_above": 1024, + "type": "keyword" + }, + "image": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "instance": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "machine": { + "properties": { + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "project": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "region": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "container": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "image": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "tag": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "type": "object" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "runtime": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "destination": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "dns": { + "properties": { + "answers": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "data": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "ttl": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "header_flags": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "op_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "question": { + "properties": { + "class": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "subdomain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "resolved_ip": { + "type": "ip" + }, + "response_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "docker": { + "properties": { + "container": { + "properties": { + "labels": { + "type": "object" + } + } + } + } + }, + "ecs": { + "properties": { + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "error": { + "properties": { + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "message": { + "norms": false, + "type": "text" + }, + "stack_trace": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "event": { + "properties": { + "action": { + "ignore_above": 1024, + "type": "keyword" + }, + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "code": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "dataset": { + "ignore_above": 1024, + "type": "keyword" + }, + "duration": { + "type": "long" + }, + "end": { + "type": "date" + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ingested": { + "type": "date" + }, + "kind": { + "ignore_above": 1024, + "type": "keyword" + }, + "module": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "ignore_above": 1024, + "type": "keyword" + }, + "outcome": { + "ignore_above": 1024, + "type": "keyword" + }, + "provider": { + "ignore_above": 1024, + "type": "keyword" + }, + "risk_score": { + "type": "float" + }, + "risk_score_norm": { + "type": "float" + }, + "sequence": { + "type": "long" + }, + "severity": { + "type": "long" + }, + "start": { + "type": "date" + }, + "timezone": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "fields": { + "type": "object" + }, + "file": { + "properties": { + "accessed": { + "type": "date" + }, + "attributes": { + "ignore_above": 1024, + "type": "keyword" + }, + "created": { + "type": "date" + }, + "ctime": { + "type": "date" + }, + "device": { + "ignore_above": 1024, + "type": "keyword" + }, + "directory": { + "ignore_above": 1024, + "type": "keyword" + }, + "drive_letter": { + "ignore_above": 1, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "inode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mode": { + "ignore_above": 1024, + "type": "keyword" + }, + "mtime": { + "type": "date" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "fields": { + "raw": { + "ignore_above": 1024, + "type": "keyword" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "owner": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "selinux": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "role": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "setgid": { + "type": "boolean" + }, + "setuid": { + "type": "boolean" + }, + "size": { + "type": "long" + }, + "target_path": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "geoip": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "properties": { + "blake2b_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "blake2b_384": { + "ignore_above": 1024, + "type": "keyword" + }, + "blake2b_512": { + "ignore_above": 1024, + "type": "keyword" + }, + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_512": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512_224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "xxh64": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "containerized": { + "type": "boolean" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "build": { + "ignore_above": 1024, + "type": "keyword" + }, + "codename": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "http": { + "properties": { + "request": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "method": { + "ignore_above": 1024, + "type": "keyword" + }, + "referrer": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "response": { + "properties": { + "body": { + "properties": { + "bytes": { + "type": "long" + }, + "content": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "bytes": { + "type": "long" + }, + "status_code": { + "type": "long" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "jolokia": { + "properties": { + "agent": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "secured": { + "type": "boolean" + }, + "server": { + "properties": { + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "vendor": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "kubernetes": { + "properties": { + "annotations": { + "properties": { + "*": { + "type": "object" + } + } + }, + "container": { + "properties": { + "image": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "deployment": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "labels": { + "properties": { + "*": { + "type": "object" + } + } + }, + "namespace": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pod": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "replicaset": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "statefulset": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "labels": { + "type": "object" + }, + "log": { + "properties": { + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "logger": { + "ignore_above": 1024, + "type": "keyword" + }, + "origin": { + "properties": { + "file": { + "properties": { + "line": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "function": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "original": { + "ignore_above": 1024, + "type": "keyword" + }, + "syslog": { + "properties": { + "facility": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "priority": { + "type": "long" + }, + "severity": { + "properties": { + "code": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "message": { + "norms": false, + "type": "text" + }, + "network": { + "properties": { + "application": { + "ignore_above": 1024, + "type": "keyword" + }, + "bytes": { + "type": "long" + }, + "community_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "direction": { + "ignore_above": 1024, + "type": "keyword" + }, + "forwarded_ip": { + "type": "ip" + }, + "iana_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "packets": { + "type": "long" + }, + "protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "transport": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "observer": { + "properties": { + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "product": { + "ignore_above": 1024, + "type": "keyword" + }, + "serial_number": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "vendor": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "organization": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "package": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "build_version": { + "ignore_above": 1024, + "type": "keyword" + }, + "checksum": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "install_scope": { + "ignore_above": 1024, + "type": "keyword" + }, + "installed": { + "type": "date" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "process": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "hash": { + "properties": { + "blake2b_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "blake2b_384": { + "ignore_above": 1024, + "type": "keyword" + }, + "blake2b_512": { + "ignore_above": 1024, + "type": "keyword" + }, + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_384": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha3_512": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512_224": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha512_256": { + "ignore_above": 1024, + "type": "keyword" + }, + "xxh64": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "parent": { + "properties": { + "args": { + "ignore_above": 1024, + "type": "keyword" + }, + "args_count": { + "type": "long" + }, + "command_line": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "executable": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "exit_code": { + "type": "long" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "pgid": { + "type": "long" + }, + "pid": { + "type": "long" + }, + "ppid": { + "type": "long" + }, + "start": { + "type": "date" + }, + "thread": { + "properties": { + "id": { + "type": "long" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "title": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "uptime": { + "type": "long" + }, + "working_directory": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "registry": { + "properties": { + "data": { + "properties": { + "bytes": { + "ignore_above": 1024, + "type": "keyword" + }, + "strings": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hive": { + "ignore_above": 1024, + "type": "keyword" + }, + "key": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "value": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "related": { + "properties": { + "ip": { + "type": "ip" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "rule": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "ruleset": { + "ignore_above": 1024, + "type": "keyword" + }, + "uuid": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "server": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "service": { + "properties": { + "ephemeral_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "node": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "state": { + "ignore_above": 1024, + "type": "keyword" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "socket": { + "properties": { + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "source": { + "properties": { + "address": { + "ignore_above": 1024, + "type": "keyword" + }, + "as": { + "properties": { + "number": { + "type": "long" + }, + "organization": { + "properties": { + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "bytes": { + "type": "long" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "geo": { + "properties": { + "city_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "continent_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "country_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "location": { + "type": "geo_point" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_iso_code": { + "ignore_above": 1024, + "type": "keyword" + }, + "region_name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "nat": { + "properties": { + "ip": { + "type": "ip" + }, + "port": { + "type": "long" + } + } + }, + "packets": { + "type": "long" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "system": { + "properties": { + "audit": { + "properties": { + "host": { + "properties": { + "architecture": { + "ignore_above": 1024, + "type": "keyword" + }, + "boottime": { + "type": "date" + }, + "containerized": { + "type": "boolean" + }, + "hostname": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "ip": { + "type": "ip" + }, + "mac": { + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "codename": { + "ignore_above": 1024, + "type": "keyword" + }, + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "timezone": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "offset": { + "properties": { + "sec": { + "type": "long" + } + } + } + } + }, + "uptime": { + "type": "long" + } + } + }, + "package": { + "properties": { + "arch": { + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "installtime": { + "type": "date" + }, + "license": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "release": { + "ignore_above": 1024, + "type": "keyword" + }, + "size": { + "type": "long" + }, + "summary": { + "ignore_above": 1024, + "type": "keyword" + }, + "url": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user": { + "properties": { + "dir": { + "ignore_above": 1024, + "type": "keyword" + }, + "gid": { + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "type": "object" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "password": { + "properties": { + "last_changed": { + "type": "date" + }, + "type": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "shell": { + "ignore_above": 1024, + "type": "keyword" + }, + "uid": { + "ignore_above": 1024, + "type": "keyword" + }, + "user_information": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + } + } + }, + "tags": { + "ignore_above": 1024, + "type": "keyword" + }, + "threat": { + "properties": { + "framework": { + "ignore_above": 1024, + "type": "keyword" + }, + "tactic": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "technique": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "timeseries": { + "properties": { + "instance": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tls": { + "properties": { + "cipher": { + "ignore_above": 1024, + "type": "keyword" + }, + "client": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "server_name": { + "ignore_above": 1024, + "type": "keyword" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + }, + "supported_ciphers": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "curve": { + "ignore_above": 1024, + "type": "keyword" + }, + "established": { + "type": "boolean" + }, + "next_protocol": { + "ignore_above": 1024, + "type": "keyword" + }, + "resumed": { + "type": "boolean" + }, + "server": { + "properties": { + "certificate": { + "ignore_above": 1024, + "type": "keyword" + }, + "certificate_chain": { + "ignore_above": 1024, + "type": "keyword" + }, + "hash": { + "properties": { + "md5": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha1": { + "ignore_above": 1024, + "type": "keyword" + }, + "sha256": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "issuer": { + "ignore_above": 1024, + "type": "keyword" + }, + "ja3s": { + "ignore_above": 1024, + "type": "keyword" + }, + "not_after": { + "type": "date" + }, + "not_before": { + "type": "date" + }, + "subject": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + }, + "version_protocol": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "tracing": { + "properties": { + "trace": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "transaction": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "url": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "extension": { + "ignore_above": 1024, + "type": "keyword" + }, + "fragment": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "password": { + "ignore_above": 1024, + "type": "keyword" + }, + "path": { + "ignore_above": 1024, + "type": "keyword" + }, + "port": { + "type": "long" + }, + "query": { + "ignore_above": 1024, + "type": "keyword" + }, + "registered_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "scheme": { + "ignore_above": 1024, + "type": "keyword" + }, + "top_level_domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "username": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user": { + "properties": { + "audit": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "effective": { + "properties": { + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "email": { + "ignore_above": 1024, + "type": "keyword" + }, + "entity_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "filesystem": { + "properties": { + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "full_name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "group": { + "properties": { + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "hash": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "name_map": { + "type": "object" + }, + "saved": { + "properties": { + "group": { + "properties": { + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "selinux": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "domain": { + "ignore_above": 1024, + "type": "keyword" + }, + "level": { + "ignore_above": 1024, + "type": "keyword" + }, + "role": { + "ignore_above": 1024, + "type": "keyword" + }, + "user": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "terminal": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "user_agent": { + "properties": { + "device": { + "properties": { + "name": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "name": { + "ignore_above": 1024, + "type": "keyword" + }, + "original": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "os": { + "properties": { + "family": { + "ignore_above": 1024, + "type": "keyword" + }, + "full": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "kernel": { + "ignore_above": 1024, + "type": "keyword" + }, + "name": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "platform": { + "ignore_above": 1024, + "type": "keyword" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "vulnerability": { + "properties": { + "category": { + "ignore_above": 1024, + "type": "keyword" + }, + "classification": { + "ignore_above": 1024, + "type": "keyword" + }, + "description": { + "fields": { + "text": { + "norms": false, + "type": "text" + } + }, + "ignore_above": 1024, + "type": "keyword" + }, + "enumeration": { + "ignore_above": 1024, + "type": "keyword" + }, + "id": { + "ignore_above": 1024, + "type": "keyword" + }, + "reference": { + "ignore_above": 1024, + "type": "keyword" + }, + "report_id": { + "ignore_above": 1024, + "type": "keyword" + }, + "scanner": { + "properties": { + "vendor": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "score": { + "properties": { + "base": { + "type": "float" + }, + "environmental": { + "type": "float" + }, + "temporal": { + "type": "float" + }, + "version": { + "ignore_above": 1024, + "type": "keyword" + } + } + }, + "severity": { + "ignore_above": 1024, + "type": "keyword" + } + } + } + } + }, + "settings": { + "index": { + "lifecycle": { + "name": "auditbeat", + "rollover_alias": "auditbeat-7.6.2" + }, + "mapping": { + "total_fields": { + "limit": "10000" + } + }, + "number_of_replicas": "1", + "number_of_shards": "1", + "query": { + "default_field": [ + "message", + "tags", + "agent.ephemeral_id", + "agent.id", + "agent.name", + "agent.type", + "agent.version", + "as.organization.name", + "client.address", + "client.as.organization.name", + "client.domain", + "client.geo.city_name", + "client.geo.continent_name", + "client.geo.country_iso_code", + "client.geo.country_name", + "client.geo.name", + "client.geo.region_iso_code", + "client.geo.region_name", + "client.mac", + "client.registered_domain", + "client.top_level_domain", + "client.user.domain", + "client.user.email", + "client.user.full_name", + "client.user.group.domain", + "client.user.group.id", + "client.user.group.name", + "client.user.hash", + "client.user.id", + "client.user.name", + "cloud.account.id", + "cloud.availability_zone", + "cloud.instance.id", + "cloud.instance.name", + "cloud.machine.type", + "cloud.provider", + "cloud.region", + "container.id", + "container.image.name", + "container.image.tag", + "container.name", + "container.runtime", + "destination.address", + "destination.as.organization.name", + "destination.domain", + "destination.geo.city_name", + "destination.geo.continent_name", + "destination.geo.country_iso_code", + "destination.geo.country_name", + "destination.geo.name", + "destination.geo.region_iso_code", + "destination.geo.region_name", + "destination.mac", + "destination.registered_domain", + "destination.top_level_domain", + "destination.user.domain", + "destination.user.email", + "destination.user.full_name", + "destination.user.group.domain", + "destination.user.group.id", + "destination.user.group.name", + "destination.user.hash", + "destination.user.id", + "destination.user.name", + "dns.answers.class", + "dns.answers.data", + "dns.answers.name", + "dns.answers.type", + "dns.header_flags", + "dns.id", + "dns.op_code", + "dns.question.class", + "dns.question.name", + "dns.question.registered_domain", + "dns.question.subdomain", + "dns.question.top_level_domain", + "dns.question.type", + "dns.response_code", + "dns.type", + "ecs.version", + "error.code", + "error.id", + "error.message", + "error.stack_trace", + "error.type", + "event.action", + "event.category", + "event.code", + "event.dataset", + "event.hash", + "event.id", + "event.kind", + "event.module", + "event.original", + "event.outcome", + "event.provider", + "event.timezone", + "event.type", + "file.device", + "file.directory", + "file.extension", + "file.gid", + "file.group", + "file.hash.md5", + "file.hash.sha1", + "file.hash.sha256", + "file.hash.sha512", + "file.inode", + "file.mode", + "file.name", + "file.owner", + "file.path", + "file.target_path", + "file.type", + "file.uid", + "geo.city_name", + "geo.continent_name", + "geo.country_iso_code", + "geo.country_name", + "geo.name", + "geo.region_iso_code", + "geo.region_name", + "group.domain", + "group.id", + "group.name", + "hash.md5", + "hash.sha1", + "hash.sha256", + "hash.sha512", + "host.architecture", + "host.geo.city_name", + "host.geo.continent_name", + "host.geo.country_iso_code", + "host.geo.country_name", + "host.geo.name", + "host.geo.region_iso_code", + "host.geo.region_name", + "host.hostname", + "host.id", + "host.mac", + "host.name", + "host.os.family", + "host.os.full", + "host.os.kernel", + "host.os.name", + "host.os.platform", + "host.os.version", + "host.type", + "host.user.domain", + "host.user.email", + "host.user.full_name", + "host.user.group.domain", + "host.user.group.id", + "host.user.group.name", + "host.user.hash", + "host.user.id", + "host.user.name", + "http.request.body.content", + "http.request.method", + "http.request.referrer", + "http.response.body.content", + "http.version", + "log.level", + "log.logger", + "log.origin.file.name", + "log.origin.function", + "log.original", + "log.syslog.facility.name", + "log.syslog.severity.name", + "network.application", + "network.community_id", + "network.direction", + "network.iana_number", + "network.name", + "network.protocol", + "network.transport", + "network.type", + "observer.geo.city_name", + "observer.geo.continent_name", + "observer.geo.country_iso_code", + "observer.geo.country_name", + "observer.geo.name", + "observer.geo.region_iso_code", + "observer.geo.region_name", + "observer.hostname", + "observer.mac", + "observer.name", + "observer.os.family", + "observer.os.full", + "observer.os.kernel", + "observer.os.name", + "observer.os.platform", + "observer.os.version", + "observer.product", + "observer.serial_number", + "observer.type", + "observer.vendor", + "observer.version", + "organization.id", + "organization.name", + "os.family", + "os.full", + "os.kernel", + "os.name", + "os.platform", + "os.version", + "package.architecture", + "package.checksum", + "package.description", + "package.install_scope", + "package.license", + "package.name", + "package.path", + "package.version", + "process.args", + "text", + "process.executable", + "process.hash.md5", + "process.hash.sha1", + "process.hash.sha256", + "process.hash.sha512", + "process.name", + "text", + "text", + "text", + "text", + "text", + "process.thread.name", + "process.title", + "process.working_directory", + "server.address", + "server.as.organization.name", + "server.domain", + "server.geo.city_name", + "server.geo.continent_name", + "server.geo.country_iso_code", + "server.geo.country_name", + "server.geo.name", + "server.geo.region_iso_code", + "server.geo.region_name", + "server.mac", + "server.registered_domain", + "server.top_level_domain", + "server.user.domain", + "server.user.email", + "server.user.full_name", + "server.user.group.domain", + "server.user.group.id", + "server.user.group.name", + "server.user.hash", + "server.user.id", + "server.user.name", + "service.ephemeral_id", + "service.id", + "service.name", + "service.node.name", + "service.state", + "service.type", + "service.version", + "source.address", + "source.as.organization.name", + "source.domain", + "source.geo.city_name", + "source.geo.continent_name", + "source.geo.country_iso_code", + "source.geo.country_name", + "source.geo.name", + "source.geo.region_iso_code", + "source.geo.region_name", + "source.mac", + "source.registered_domain", + "source.top_level_domain", + "source.user.domain", + "source.user.email", + "source.user.full_name", + "source.user.group.domain", + "source.user.group.id", + "source.user.group.name", + "source.user.hash", + "source.user.id", + "source.user.name", + "threat.framework", + "threat.tactic.id", + "threat.tactic.name", + "threat.tactic.reference", + "threat.technique.id", + "threat.technique.name", + "threat.technique.reference", + "tracing.trace.id", + "tracing.transaction.id", + "url.domain", + "url.extension", + "url.fragment", + "url.full", + "url.original", + "url.password", + "url.path", + "url.query", + "url.registered_domain", + "url.scheme", + "url.top_level_domain", + "url.username", + "user.domain", + "user.email", + "user.full_name", + "user.group.domain", + "user.group.id", + "user.group.name", + "user.hash", + "user.id", + "user.name", + "user_agent.device.name", + "user_agent.name", + "text", + "user_agent.original", + "user_agent.os.family", + "user_agent.os.full", + "user_agent.os.kernel", + "user_agent.os.name", + "user_agent.os.platform", + "user_agent.os.version", + "user_agent.version", + "text", + "agent.hostname", + "timeseries.instance", + "cloud.project.id", + "cloud.image.id", + "host.os.build", + "host.os.codename", + "kubernetes.pod.name", + "kubernetes.pod.uid", + "kubernetes.namespace", + "kubernetes.node.name", + "kubernetes.replicaset.name", + "kubernetes.deployment.name", + "kubernetes.statefulset.name", + "kubernetes.container.name", + "kubernetes.container.image", + "jolokia.agent.version", + "jolokia.agent.id", + "jolokia.server.product", + "jolokia.server.version", + "jolokia.server.vendor", + "jolokia.url", + "raw", + "file.origin", + "file.selinux.user", + "file.selinux.role", + "file.selinux.domain", + "file.selinux.level", + "user.audit.id", + "user.audit.name", + "user.effective.id", + "user.effective.name", + "user.effective.group.id", + "user.effective.group.name", + "user.filesystem.id", + "user.filesystem.name", + "user.filesystem.group.id", + "user.filesystem.group.name", + "user.saved.id", + "user.saved.name", + "user.saved.group.id", + "user.saved.group.name", + "user.selinux.user", + "user.selinux.role", + "user.selinux.domain", + "user.selinux.level", + "user.selinux.category", + "source.path", + "destination.path", + "auditd.message_type", + "auditd.session", + "auditd.result", + "auditd.summary.actor.primary", + "auditd.summary.actor.secondary", + "auditd.summary.object.type", + "auditd.summary.object.primary", + "auditd.summary.object.secondary", + "auditd.summary.how", + "auditd.paths.inode", + "auditd.paths.dev", + "auditd.paths.obj_user", + "auditd.paths.obj_role", + "auditd.paths.obj_domain", + "auditd.paths.obj_level", + "auditd.paths.objtype", + "auditd.paths.ouid", + "auditd.paths.rdev", + "auditd.paths.nametype", + "auditd.paths.ogid", + "auditd.paths.item", + "auditd.paths.mode", + "auditd.paths.name", + "auditd.data.action", + "auditd.data.minor", + "auditd.data.acct", + "auditd.data.addr", + "auditd.data.cipher", + "auditd.data.id", + "auditd.data.entries", + "auditd.data.kind", + "auditd.data.ksize", + "auditd.data.spid", + "auditd.data.arch", + "auditd.data.argc", + "auditd.data.major", + "auditd.data.unit", + "auditd.data.table", + "auditd.data.terminal", + "auditd.data.grantors", + "auditd.data.direction", + "auditd.data.op", + "auditd.data.tty", + "auditd.data.syscall", + "auditd.data.data", + "auditd.data.family", + "auditd.data.mac", + "auditd.data.pfs", + "auditd.data.items", + "auditd.data.a0", + "auditd.data.a1", + "auditd.data.a2", + "auditd.data.a3", + "auditd.data.hostname", + "auditd.data.lport", + "auditd.data.rport", + "auditd.data.exit", + "auditd.data.fp", + "auditd.data.laddr", + "auditd.data.sport", + "auditd.data.capability", + "auditd.data.nargs", + "auditd.data.new-enabled", + "auditd.data.audit_backlog_limit", + "auditd.data.dir", + "auditd.data.cap_pe", + "auditd.data.model", + "auditd.data.new_pp", + "auditd.data.old-enabled", + "auditd.data.oauid", + "auditd.data.old", + "auditd.data.banners", + "auditd.data.feature", + "auditd.data.vm-ctx", + "auditd.data.opid", + "auditd.data.seperms", + "auditd.data.seresult", + "auditd.data.new-rng", + "auditd.data.old-net", + "auditd.data.sigev_signo", + "auditd.data.ino", + "auditd.data.old_enforcing", + "auditd.data.old-vcpu", + "auditd.data.range", + "auditd.data.res", + "auditd.data.added", + "auditd.data.fam", + "auditd.data.nlnk-pid", + "auditd.data.subj", + "auditd.data.a[0-3]", + "auditd.data.cgroup", + "auditd.data.kernel", + "auditd.data.ocomm", + "auditd.data.new-net", + "auditd.data.permissive", + "auditd.data.class", + "auditd.data.compat", + "auditd.data.fi", + "auditd.data.changed", + "auditd.data.msg", + "auditd.data.dport", + "auditd.data.new-seuser", + "auditd.data.invalid_context", + "auditd.data.dmac", + "auditd.data.ipx-net", + "auditd.data.iuid", + "auditd.data.macproto", + "auditd.data.obj", + "auditd.data.ipid", + "auditd.data.new-fs", + "auditd.data.vm-pid", + "auditd.data.cap_pi", + "auditd.data.old-auid", + "auditd.data.oses", + "auditd.data.fd", + "auditd.data.igid", + "auditd.data.new-disk", + "auditd.data.parent", + "auditd.data.len", + "auditd.data.oflag", + "auditd.data.uuid", + "auditd.data.code", + "auditd.data.nlnk-grp", + "auditd.data.cap_fp", + "auditd.data.new-mem", + "auditd.data.seperm", + "auditd.data.enforcing", + "auditd.data.new-chardev", + "auditd.data.old-rng", + "auditd.data.outif", + "auditd.data.cmd", + "auditd.data.hook", + "auditd.data.new-level", + "auditd.data.sauid", + "auditd.data.sig", + "auditd.data.audit_backlog_wait_time", + "auditd.data.printer", + "auditd.data.old-mem", + "auditd.data.perm", + "auditd.data.old_pi", + "auditd.data.state", + "auditd.data.format", + "auditd.data.new_gid", + "auditd.data.tcontext", + "auditd.data.maj", + "auditd.data.watch", + "auditd.data.device", + "auditd.data.grp", + "auditd.data.bool", + "auditd.data.icmp_type", + "auditd.data.new_lock", + "auditd.data.old_prom", + "auditd.data.acl", + "auditd.data.ip", + "auditd.data.new_pi", + "auditd.data.default-context", + "auditd.data.inode_gid", + "auditd.data.new-log_passwd", + "auditd.data.new_pe", + "auditd.data.selected-context", + "auditd.data.cap_fver", + "auditd.data.file", + "auditd.data.net", + "auditd.data.virt", + "auditd.data.cap_pp", + "auditd.data.old-range", + "auditd.data.resrc", + "auditd.data.new-range", + "auditd.data.obj_gid", + "auditd.data.proto", + "auditd.data.old-disk", + "auditd.data.audit_failure", + "auditd.data.inif", + "auditd.data.vm", + "auditd.data.flags", + "auditd.data.nlnk-fam", + "auditd.data.old-fs", + "auditd.data.old-ses", + "auditd.data.seqno", + "auditd.data.fver", + "auditd.data.qbytes", + "auditd.data.seuser", + "auditd.data.cap_fe", + "auditd.data.new-vcpu", + "auditd.data.old-level", + "auditd.data.old_pp", + "auditd.data.daddr", + "auditd.data.old-role", + "auditd.data.ioctlcmd", + "auditd.data.smac", + "auditd.data.apparmor", + "auditd.data.fe", + "auditd.data.perm_mask", + "auditd.data.ses", + "auditd.data.cap_fi", + "auditd.data.obj_uid", + "auditd.data.reason", + "auditd.data.list", + "auditd.data.old_lock", + "auditd.data.bus", + "auditd.data.old_pe", + "auditd.data.new-role", + "auditd.data.prom", + "auditd.data.uri", + "auditd.data.audit_enabled", + "auditd.data.old-log_passwd", + "auditd.data.old-seuser", + "auditd.data.per", + "auditd.data.scontext", + "auditd.data.tclass", + "auditd.data.ver", + "auditd.data.new", + "auditd.data.val", + "auditd.data.img-ctx", + "auditd.data.old-chardev", + "auditd.data.old_val", + "auditd.data.success", + "auditd.data.inode_uid", + "auditd.data.removed", + "auditd.data.socket.port", + "auditd.data.socket.saddr", + "auditd.data.socket.addr", + "auditd.data.socket.family", + "auditd.data.socket.path", + "geoip.continent_name", + "geoip.city_name", + "geoip.region_name", + "geoip.country_iso_code", + "hash.blake2b_256", + "hash.blake2b_384", + "hash.blake2b_512", + "hash.md5", + "hash.sha1", + "hash.sha224", + "hash.sha256", + "hash.sha384", + "hash.sha3_224", + "hash.sha3_256", + "hash.sha3_384", + "hash.sha3_512", + "hash.sha512", + "hash.sha512_224", + "hash.sha512_256", + "hash.xxh64", + "event.origin", + "user.entity_id", + "user.terminal", + "process.entity_id", + "process.hash.blake2b_256", + "process.hash.blake2b_384", + "process.hash.blake2b_512", + "process.hash.sha224", + "process.hash.sha384", + "process.hash.sha3_224", + "process.hash.sha3_256", + "process.hash.sha3_384", + "process.hash.sha3_512", + "process.hash.sha512_224", + "process.hash.sha512_256", + "process.hash.xxh64", + "socket.entity_id", + "system.audit.host.timezone.name", + "system.audit.host.hostname", + "system.audit.host.id", + "system.audit.host.architecture", + "system.audit.host.mac", + "system.audit.host.os.codename", + "system.audit.host.os.platform", + "system.audit.host.os.name", + "system.audit.host.os.family", + "system.audit.host.os.version", + "system.audit.host.os.kernel", + "system.audit.package.entity_id", + "system.audit.package.name", + "system.audit.package.version", + "system.audit.package.release", + "system.audit.package.arch", + "system.audit.package.license", + "system.audit.package.summary", + "system.audit.package.url", + "system.audit.user.name", + "system.audit.user.uid", + "system.audit.user.gid", + "system.audit.user.dir", + "system.audit.user.shell", + "system.audit.user.user_information", + "system.audit.user.password.type", + "fields.*" + ] + }, + "refresh_interval": "5s" + } + } + } +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index d9edb55a320395..3f04b2d26a0139 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1343,27 +1343,22 @@ resolved "https://registry.yarnpkg.com/@elastic/maki/-/maki-6.2.0.tgz#d0a85aa248bdc14dca44e1f9430c0b670f65e489" integrity sha512-QkmRNpEY4Dy6eqwDimR5X9leMgdPFjdANmpEIwEW1XVUG2U4YtB2BXhDxsnMmNTUrJUjtnjnwgwBUyg0pU0FTg== -"@elastic/node-crypto@^0.1.2": - version "0.1.2" - resolved "https://registry.yarnpkg.com/@elastic/node-crypto/-/node-crypto-0.1.2.tgz#c18ac282f635e88f041cc1555d806e492ca8f3b1" - integrity sha1-wYrCgvY16I8EHMFVXYBuSSyo87E= - -"@elastic/node-crypto@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@elastic/node-crypto/-/node-crypto-1.0.0.tgz#4d325df333fe1319556bb4d54214098ada1171d4" - integrity sha512-bbjbEyILPRTRt0xnda18OttLtlkJBPuXx3CjISUSn9jhWqHoFMzfOaZ73D5jxZE2SaFZUrJYfPpqXP6qqPufAQ== +"@elastic/node-crypto@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@elastic/node-crypto/-/node-crypto-1.1.1.tgz#619b70322c9cce4a7ee5fbf8f678b1baa7f06095" + integrity sha512-F6tIk8Txdqjg8Siv60iAvXzO9ZdQI87K3sS/fh5xd2XaWK+T5ZfqeTvsT7srwG6fr6uCBfuQEJV1KBBl+JpLZA== "@elastic/numeral@2.4.0": version "2.4.0" resolved "https://registry.yarnpkg.com/@elastic/numeral/-/numeral-2.4.0.tgz#883197b7f4bf3c2dd994f53b274769ddfa2bf79a" integrity sha512-uGBKGCNghTgUZPHClji/00v+AKt5nidPTGOIbcT+lbTPVxNB6QPpPLGWtXyrg3QZAxobPM/LAZB1mAqtJeq44Q== -"@elastic/request-crypto@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@elastic/request-crypto/-/request-crypto-1.0.2.tgz#bf27bf009227166f3eeb2b5193a108752335ebd3" - integrity sha512-8FtGYl7LebhmJmEDWiGn3MorvNiGWSYPqhvgRlKXjNakEuLoPBBe0DHxbwLkj08CMLWczXcO2ixqBPY7fEhJpA== +"@elastic/request-crypto@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@elastic/request-crypto/-/request-crypto-1.1.2.tgz#2e323550f546f6286994126d462a9ea480a3bfb1" + integrity sha512-i73wjj1Qi8dGJIy170Z8xyJ760mFNjTbdmcp/nEczqWD0miNW6I5wZ5MNrv7M6CXn2m1wMXiT6qzDYd93Hv1Dw== dependencies: - "@elastic/node-crypto" "^0.1.2" + "@elastic/node-crypto" "1.1.1" "@types/node-jose" "1.1.0" node-jose "1.1.0" @@ -2504,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" @@ -3403,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" @@ -3651,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" @@ -4020,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" @@ -4151,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" @@ -4622,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" @@ -4637,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" @@ -7363,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" @@ -7903,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= @@ -8050,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" @@ -8063,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" @@ -8891,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= @@ -9155,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" @@ -10580,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== @@ -10652,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" @@ -10803,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" @@ -13244,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" @@ -14721,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" @@ -15821,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" @@ -16859,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" @@ -18129,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" @@ -18364,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== @@ -18540,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" @@ -19508,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" @@ -20168,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" @@ -20596,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" @@ -20708,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== @@ -21211,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" @@ -21896,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" @@ -21908,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== @@ -24957,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" @@ -25696,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" @@ -26254,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" @@ -28653,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" @@ -28932,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" @@ -29436,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" @@ -30126,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" @@ -31282,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" @@ -31580,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" @@ -31597,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"