From 4c893985e3a89a3458c328e48a1543a9022ecf41 Mon Sep 17 00:00:00 2001 From: Marshall Main <55718608+marshallmain@users.noreply.github.com> Date: Wed, 3 Mar 2021 12:02:08 -0800 Subject: [PATCH 01/14] [Security Solution][Lists] Escape quotes in list ids and quote the id in KQL query (#93176) * Escape quotes in list ids and quote the id in KQL query * Remove decodeURIComponent because too many KQL queries don't handle quotes * Add quotes to user supplied IDs for other KQL queries Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../lists/server/routes/delete_list_route.ts | 3 ++- .../find_exception_list_items.test.ts | 25 +++++++++++++------ .../find_exception_list_items.ts | 7 ++++-- .../server/services/utils/escape_query.ts | 10 ++++++++ .../services/utils/get_query_filter.test.ts | 4 +-- .../server/services/utils/get_query_filter.ts | 7 +++++- .../security_and_spaces/tests/delete_lists.ts | 22 ++++++++++++++++ 7 files changed, 65 insertions(+), 13 deletions(-) create mode 100644 x-pack/plugins/lists/server/services/utils/escape_query.ts diff --git a/x-pack/plugins/lists/server/routes/delete_list_route.ts b/x-pack/plugins/lists/server/routes/delete_list_route.ts index 4732b25dbf5e..3e9b76a1b330 100644 --- a/x-pack/plugins/lists/server/routes/delete_list_route.ts +++ b/x-pack/plugins/lists/server/routes/delete_list_route.ts @@ -19,6 +19,7 @@ import { } from '../../common/schemas'; import { getSavedObjectType } from '../services/exception_lists/utils'; import { ExceptionListClient } from '../services/exception_lists/exception_list_client'; +import { escapeQuotes } from '../services/utils/escape_query'; import { getExceptionListClient, getListClient } from '.'; @@ -142,7 +143,7 @@ const getReferencedExceptionLists = async ( (item) => `${getSavedObjectType({ namespaceType: item.namespace_type, - })}.attributes.list_id: ${item.list_id}` + })}.attributes.list_id: "${escapeQuotes(item.list_id)}"` ) .join(' OR '); return exceptionLists.findExceptionList({ diff --git a/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_items.test.ts b/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_items.test.ts index 0d3dd2d9b65c..3a2b12c35891 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_items.test.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_items.test.ts @@ -18,7 +18,18 @@ describe('find_exception_list_items', () => { savedObjectType: ['exception-list'], }); expect(filter).toEqual( - '(exception-list.attributes.list_type: item AND exception-list.attributes.list_id: some-list-id)' + '(exception-list.attributes.list_type: item AND exception-list.attributes.list_id: "some-list-id")' + ); + }); + + test('It should create a filter escaping quotes in list ids', () => { + const filter = getExceptionListsItemFilter({ + filter: [], + listId: ['list-id-"-with-quote'], + savedObjectType: ['exception-list'], + }); + expect(filter).toEqual( + '(exception-list.attributes.list_type: item AND exception-list.attributes.list_id: "list-id-\\"-with-quote")' ); }); @@ -29,7 +40,7 @@ describe('find_exception_list_items', () => { savedObjectType: ['exception-list'], }); expect(filter).toEqual( - '((exception-list.attributes.list_type: item AND exception-list.attributes.list_id: some-list-id) AND exception-list.attributes.name: "Sample Endpoint Exception List")' + '((exception-list.attributes.list_type: item AND exception-list.attributes.list_id: "some-list-id") AND exception-list.attributes.name: "Sample Endpoint Exception List")' ); }); @@ -40,7 +51,7 @@ describe('find_exception_list_items', () => { savedObjectType: ['exception-list', 'exception-list-agnostic'], }); expect(filter).toEqual( - '(exception-list.attributes.list_type: item AND exception-list.attributes.list_id: list-1) OR (exception-list-agnostic.attributes.list_type: item AND exception-list-agnostic.attributes.list_id: list-2)' + '(exception-list.attributes.list_type: item AND exception-list.attributes.list_id: "list-1") OR (exception-list-agnostic.attributes.list_type: item AND exception-list-agnostic.attributes.list_id: "list-2")' ); }); @@ -51,7 +62,7 @@ describe('find_exception_list_items', () => { savedObjectType: ['exception-list', 'exception-list-agnostic'], }); expect(filter).toEqual( - '((exception-list.attributes.list_type: item AND exception-list.attributes.list_id: list-1) AND exception-list.attributes.name: "Sample Endpoint Exception List") OR (exception-list-agnostic.attributes.list_type: item AND exception-list-agnostic.attributes.list_id: list-2)' + '((exception-list.attributes.list_type: item AND exception-list.attributes.list_id: "list-1") AND exception-list.attributes.name: "Sample Endpoint Exception List") OR (exception-list-agnostic.attributes.list_type: item AND exception-list-agnostic.attributes.list_id: "list-2")' ); }); @@ -62,7 +73,7 @@ describe('find_exception_list_items', () => { savedObjectType: ['exception-list', 'exception-list-agnostic', 'exception-list-agnostic'], }); expect(filter).toEqual( - '(exception-list.attributes.list_type: item AND exception-list.attributes.list_id: list-1) OR (exception-list-agnostic.attributes.list_type: item AND exception-list-agnostic.attributes.list_id: list-2) OR (exception-list-agnostic.attributes.list_type: item AND exception-list-agnostic.attributes.list_id: list-3)' + '(exception-list.attributes.list_type: item AND exception-list.attributes.list_id: "list-1") OR (exception-list-agnostic.attributes.list_type: item AND exception-list-agnostic.attributes.list_id: "list-2") OR (exception-list-agnostic.attributes.list_type: item AND exception-list-agnostic.attributes.list_id: "list-3")' ); }); @@ -73,7 +84,7 @@ describe('find_exception_list_items', () => { savedObjectType: ['exception-list', 'exception-list-agnostic', 'exception-list-agnostic'], }); expect(filter).toEqual( - '((exception-list.attributes.list_type: item AND exception-list.attributes.list_id: list-1) AND exception-list.attributes.name: "Sample Endpoint Exception List") OR (exception-list-agnostic.attributes.list_type: item AND exception-list-agnostic.attributes.list_id: list-2) OR (exception-list-agnostic.attributes.list_type: item AND exception-list-agnostic.attributes.list_id: list-3)' + '((exception-list.attributes.list_type: item AND exception-list.attributes.list_id: "list-1") AND exception-list.attributes.name: "Sample Endpoint Exception List") OR (exception-list-agnostic.attributes.list_type: item AND exception-list-agnostic.attributes.list_id: "list-2") OR (exception-list-agnostic.attributes.list_type: item AND exception-list-agnostic.attributes.list_id: "list-3")' ); }); @@ -88,7 +99,7 @@ describe('find_exception_list_items', () => { savedObjectType: ['exception-list', 'exception-list-agnostic', 'exception-list-agnostic'], }); expect(filter).toEqual( - '((exception-list.attributes.list_type: item AND exception-list.attributes.list_id: list-1) AND exception-list.attributes.name: "Sample Endpoint Exception List 1") OR ((exception-list-agnostic.attributes.list_type: item AND exception-list-agnostic.attributes.list_id: list-2) AND exception-list.attributes.name: "Sample Endpoint Exception List 2") OR ((exception-list-agnostic.attributes.list_type: item AND exception-list-agnostic.attributes.list_id: list-3) AND exception-list.attributes.name: "Sample Endpoint Exception List 3")' + '((exception-list.attributes.list_type: item AND exception-list.attributes.list_id: "list-1") AND exception-list.attributes.name: "Sample Endpoint Exception List 1") OR ((exception-list-agnostic.attributes.list_type: item AND exception-list-agnostic.attributes.list_id: "list-2") AND exception-list.attributes.name: "Sample Endpoint Exception List 2") OR ((exception-list-agnostic.attributes.list_type: item AND exception-list-agnostic.attributes.list_id: "list-3") AND exception-list.attributes.name: "Sample Endpoint Exception List 3")' ); }); }); diff --git a/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_items.ts b/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_items.ts index cc84314eaa7a..155408dafc79 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_items.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/find_exception_list_items.ts @@ -24,6 +24,7 @@ import { SortFieldOrUndefined, SortOrderOrUndefined, } from '../../../common/schemas'; +import { escapeQuotes } from '../utils/escape_query'; import { getSavedObjectTypes, transformSavedObjectsToFoundExceptionListItem } from './utils'; import { getExceptionList } from './get_exception_list'; @@ -89,7 +90,8 @@ export const getExceptionListsItemFilter = ({ savedObjectType: SavedObjectType[]; }): string => { return listId.reduce((accum, singleListId, index) => { - const listItemAppend = `(${savedObjectType[index]}.attributes.list_type: item AND ${savedObjectType[index]}.attributes.list_id: ${singleListId})`; + const escapedListId = escapeQuotes(singleListId); + const listItemAppend = `(${savedObjectType[index]}.attributes.list_type: item AND ${savedObjectType[index]}.attributes.list_id: "${escapedListId}")`; const listItemAppendWithFilter = filter[index] != null ? `(${listItemAppend} AND ${filter[index]})` : listItemAppend; if (accum === '') { @@ -117,8 +119,9 @@ export const findValueListExceptionListItems = async ({ sortField, sortOrder, }: FindValueListExceptionListsItems): Promise => { + const escapedValueListId = escapeQuotes(valueListId); const savedObjectsFindResponse = await savedObjectsClient.find({ - filter: `(exception-list.attributes.list_type: item AND exception-list.attributes.entries.list.id:${valueListId}) OR (exception-list-agnostic.attributes.list_type: item AND exception-list-agnostic.attributes.entries.list.id:${valueListId}) `, + filter: `(exception-list.attributes.list_type: item AND exception-list.attributes.entries.list.id:"${escapedValueListId}") OR (exception-list-agnostic.attributes.list_type: item AND exception-list-agnostic.attributes.entries.list.id:"${escapedValueListId}") `, page, perPage, sortField, diff --git a/x-pack/plugins/lists/server/services/utils/escape_query.ts b/x-pack/plugins/lists/server/services/utils/escape_query.ts new file mode 100644 index 000000000000..f654b8a2b9eb --- /dev/null +++ b/x-pack/plugins/lists/server/services/utils/escape_query.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const escapeQuotes = (str: string): string => { + return str.replace(/[\\"]/g, '\\$&'); +}; diff --git a/x-pack/plugins/lists/server/services/utils/get_query_filter.test.ts b/x-pack/plugins/lists/server/services/utils/get_query_filter.test.ts index d189012aec0e..0f6cc171bc04 100644 --- a/x-pack/plugins/lists/server/services/utils/get_query_filter.test.ts +++ b/x-pack/plugins/lists/server/services/utils/get_query_filter.test.ts @@ -46,7 +46,7 @@ describe('get_query_filter', () => { minimum_should_match: 1, should: [ { - match: { + match_phrase: { list_id: 'list-123', }, }, @@ -74,7 +74,7 @@ describe('get_query_filter', () => { minimum_should_match: 1, should: [ { - match: { + match_phrase: { list_id: 'list-123', }, }, diff --git a/x-pack/plugins/lists/server/services/utils/get_query_filter.ts b/x-pack/plugins/lists/server/services/utils/get_query_filter.ts index 5cbad8c284a5..25c8f9880063 100644 --- a/x-pack/plugins/lists/server/services/utils/get_query_filter.ts +++ b/x-pack/plugins/lists/server/services/utils/get_query_filter.ts @@ -9,6 +9,8 @@ import { DslQuery, EsQueryConfig } from 'src/plugins/data/common'; import { Filter, Query, esQuery } from '../../../../../../src/plugins/data/server'; +import { escapeQuotes } from './escape_query'; + export interface GetQueryFilterOptions { filter: string; } @@ -41,7 +43,10 @@ export const getQueryFilterWithListId = ({ filter, listId, }: GetQueryFilterWithListIdOptions): GetQueryFilterReturn => { + const escapedListId = escapeQuotes(listId); const filterWithListId = - filter.trim() !== '' ? `list_id: ${listId} AND (${filter})` : `list_id: ${listId}`; + filter.trim() !== '' + ? `list_id: "${escapedListId}" AND (${filter})` + : `list_id: "${escapedListId}"`; return getQueryFilter({ filter: filterWithListId }); }; diff --git a/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_lists.ts b/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_lists.ts index adba0a2f626e..4ce3c7f0e566 100644 --- a/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_lists.ts +++ b/x-pack/test/lists_api_integration/security_and_spaces/tests/delete_lists.ts @@ -63,6 +63,28 @@ export default ({ getService }: FtrProviderContext) => { expect(bodyToCompare).to.eql(getListResponseMockWithoutAutoGeneratedValues()); }); + it('should delete a single list with a list id containing non-alphanumeric characters', async () => { + // create a list + const id = `some""-list-id"(1)`; + await supertest + .post(LIST_URL) + .set('kbn-xsrf', 'true') + .send({ + ...getCreateMinimalListSchemaMock(), + id, + }) + .expect(200); + + // delete the list by its list id + const { body } = await supertest + .delete(`${LIST_URL}?id=${id}`) + .set('kbn-xsrf', 'true') + .expect(200); + + const bodyToCompare = removeListServerGeneratedProperties(body); + expect(bodyToCompare).to.eql(getListResponseMockWithoutAutoGeneratedValues()); + }); + it('should delete a single list using an auto generated id', async () => { // add a list const { body: bodyWithCreatedList } = await supertest From a620179151e3db3d1d23744f8a87d705e930f80c Mon Sep 17 00:00:00 2001 From: Thomas Neirynck Date: Wed, 3 Mar 2021 15:04:38 -0500 Subject: [PATCH 02/14] remove opacity from maps legacy style (#93456) --- src/plugins/maps_legacy/public/map/_legend.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/maps_legacy/public/map/_legend.scss b/src/plugins/maps_legacy/public/map/_legend.scss index 2c50e214c248..27016840cfab 100644 --- a/src/plugins/maps_legacy/public/map/_legend.scss +++ b/src/plugins/maps_legacy/public/map/_legend.scss @@ -1,6 +1,6 @@ .visMapLegend { @include fontSize(11px); - @include euiBottomShadowMedium($color: $euiShadowColorLarge, $opacity: .1); + @include euiBottomShadowMedium($color: $euiShadowColorLarge); font-family: $euiFontFamily; font-weight: $euiFontWeightMedium; line-height: $euiLineHeight; From c2bfa8766416bdfba400255088640deba7cd601c Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Wed, 3 Mar 2021 13:37:20 -0700 Subject: [PATCH 03/14] [core-new-docs] Adds a dev-doc for core documentation (#92976) --- dev_docs/kibana_platform_plugin_intro.mdx | 4 +-- dev_docs/kibana_server_core_components.mdx | 30 ++++++++++++++++++++++ src/core/CONVENTIONS.md | 8 +++--- src/core/CORE_CONVENTIONS.md | 16 ++++++------ src/core/README.md | 10 ++++---- 5 files changed, 49 insertions(+), 19 deletions(-) create mode 100644 dev_docs/kibana_server_core_components.mdx diff --git a/dev_docs/kibana_platform_plugin_intro.mdx b/dev_docs/kibana_platform_plugin_intro.mdx index bf009a3c5251..f70c42cb520c 100644 --- a/dev_docs/kibana_platform_plugin_intro.mdx +++ b/dev_docs/kibana_platform_plugin_intro.mdx @@ -68,7 +68,7 @@ We will continue to focus on adding clarity around these types of services and w ### Core services -Sometimes referred to just as Core, Core services provide the most basic and fundamental tools neccessary for building a plugin, like creating saved objects, +Sometimes referred to just as provide the most basic and fundamental tools neccessary for building a plugin, like creating saved objects, routing, application registration, notifications and . The Core platform is not a plugin itself, although there are some plugins that provide platform functionality. We call these . @@ -141,4 +141,4 @@ plugins to customize the Kibana experience. Examples of extension points are: ## Follow up material -Learn how to build your own plugin by following +Learn how to build your own plugin by following . diff --git a/dev_docs/kibana_server_core_components.mdx b/dev_docs/kibana_server_core_components.mdx new file mode 100644 index 000000000000..701043059be0 --- /dev/null +++ b/dev_docs/kibana_server_core_components.mdx @@ -0,0 +1,30 @@ +--- +id: kibServerAndCoreComponents +slug: /kibana-dev-docs/core-intro +title: Core components +summary: An introduction to the Kibana server and core components. +date: 2021-02-26 +tags: ['kibana','onboarding', 'dev', 'architecture'] +--- + +Core is a set of systems (frontend, backend etc.) that Kibana and its plugins are built on top of. + +## Integration with the "legacy" Kibana + +Most of the existing core functionality is still spread over "legacy" Kibana and it will take some time to upgrade it. +Kibana is started using existing "legacy" CLI that bootstraps `core` which in turn creates the "legacy" Kibana server. +At the moment `core` manages HTTP connections, handles TLS configuration and base path proxy. All requests to Kibana server +will hit HTTP server exposed by the `core` first and it will decide whether request can be solely handled by the new +platform or request should be proxied to the "legacy" Kibana. This setup allows `core` to gradually introduce any "pre-route" +processing logic, expose new routes or replace old ones handled by the "legacy" Kibana currently. + +Once config has been loaded and some of its parts were validated by the `core` it's passed to the "legacy" Kibana where +it will be additionally validated so that we can make config validation stricter with the new config validation system. +Even though the new validation system provided by the `core` is also based on Joi internally it is complemented with custom +rules tailored to our needs (e.g. `byteSize`, `duration` etc.). That means that config values that were previously accepted +by the "legacy" Kibana may be rejected by the `core` now. + +### Logging +`core` has its own and will output log records directly (e.g. to file or terminal) when configured. When no specific configuration is provided, logs are forwarded to the "legacy" Kibana so that they look the same as the rest of the +log records throughout Kibana. + diff --git a/src/core/CONVENTIONS.md b/src/core/CONVENTIONS.md index 6a519d44de0d..67476af87b6d 100644 --- a/src/core/CONVENTIONS.md +++ b/src/core/CONVENTIONS.md @@ -202,14 +202,14 @@ export class MyPlugin implements Plugin { } ``` -Prefer the pattern shown above, using `core.getStartServices()`, rather than store local references retrieved from `start`. +Prefer the pattern shown above, using `core.getStartServices()`, rather than store local references retrieved from `start`. **Bad:** ```ts export class MyPlugin implements Plugin { // Anti pattern private coreStart?: CoreStart; - private depsStart?: DepsStart; + private depsStart?: DepsStart; public setup(core) { core.application.register({ @@ -220,7 +220,7 @@ export class MyPlugin implements Plugin { return renderApp(this.coreStart, this.depsStart, params); } }); - } + } public start(core, deps) { // Anti pattern @@ -361,5 +361,5 @@ Migration example from the legacy format is available in `src/core/MIGRATION_EXA ### Naming conventions -Export start and setup contracts as `MyPluginStart` and `MyPluginSetup`. +Export start and setup contracts as `MyPluginStart` and `MyPluginSetup`. This avoids naming clashes, if everyone exported them simply as `Start` and `Setup`. diff --git a/src/core/CORE_CONVENTIONS.md b/src/core/CORE_CONVENTIONS.md index 76f3be159525..1cd997d570b6 100644 --- a/src/core/CORE_CONVENTIONS.md +++ b/src/core/CORE_CONVENTIONS.md @@ -15,7 +15,7 @@ area of Core API's and does not apply to internal types. - 1.1 All API types must be exported from the top-level `server` or `public` directories. - + ```ts // -- good -- import { IRouter } from 'src/core/server'; @@ -23,15 +23,15 @@ area of Core API's and does not apply to internal types. // -- bad -- import { IRouter } from 'src/core/server/http/router.ts'; ``` - + > Why? This is required for generating documentation from our inline > typescript doc comments, makes it easier for API consumers to find the > relevant types and creates a clear distinction between external and > internal types. - + - 1.2 Classes must not be exposed directly. Instead, use a separate type, prefixed with an 'I', to describe the public contract of the class. - + ```ts // -- good (alternative 1) -- /** @@ -66,14 +66,14 @@ area of Core API's and does not apply to internal types. ``` > Why? Classes' private members form part of their type signature making it - > impossible to mock a dependency typed as a `class`. + > impossible to mock a dependency typed as a `class`. > > Until we can use ES private field support in Typescript 3.8 > https://github.com/elastic/kibana/issues/54906 we have two alternatives > each with their own pro's and cons: > > #### Using a derived class (alternative 1) - > + > > Pro's: > - TSDoc comments are located with the source code > - The class acts as a single source of type information @@ -81,12 +81,12 @@ area of Core API's and does not apply to internal types. > Con's: > - "Go to definition" first takes you to where the type gets derived > requiring a second "Go to definition" to navigate to the type source. - > + > > #### Using a separate interface (alternative 2) > Pro's: > - Creates an explicit external API contract > - "Go to definition" will take you directly to the type definition. - > + > > Con's: > - TSDoc comments are located with the interface not next to the > implementation source code. diff --git a/src/core/README.md b/src/core/README.md index 799cdb5c799a..4dd045f37b48 100644 --- a/src/core/README.md +++ b/src/core/README.md @@ -9,7 +9,7 @@ Core Plugin API Documentation: - [Conventions for Plugins](./CONVENTIONS.md) - [Testing Kibana Plugins](./TESTING.md) - [Kibana Platform Plugin API](./docs/developer/architecture/kibana-platform-plugin-api.asciidoc ) - + Internal Documentation: - [Saved Objects Migrations](./server/saved_objects/migrations/README.md) @@ -18,18 +18,18 @@ Internal Documentation: Most of the existing core functionality is still spread over "legacy" Kibana and it will take some time to upgrade it. Kibana is started using existing "legacy" CLI that bootstraps `core` which in turn creates the "legacy" Kibana server. At the moment `core` manages HTTP connections, handles TLS configuration and base path proxy. All requests to Kibana server -will hit HTTP server exposed by the `core` first and it will decide whether request can be solely handled by the new +will hit HTTP server exposed by the `core` first and it will decide whether request can be solely handled by the new platform or request should be proxied to the "legacy" Kibana. This setup allows `core` to gradually introduce any "pre-route" processing logic, expose new routes or replace old ones handled by the "legacy" Kibana currently. -Once config has been loaded and some of its parts were validated by the `core` it's passed to the "legacy" Kibana where +Once config has been loaded and some of its parts were validated by the `core` it's passed to the "legacy" Kibana where it will be additionally validated so that we can make config validation stricter with the new config validation system. -Even though the new validation system provided by the `core` is also based on Joi internally it is complemented with custom +Even though the new validation system provided by the `core` is also based on Joi internally it is complemented with custom rules tailored to our needs (e.g. `byteSize`, `duration` etc.). That means that config values that were previously accepted by the "legacy" Kibana may be rejected by the `core` now. ### Logging -`core` has its own [logging system](./server/logging/README.mdx) and will output log records directly (e.g. to file or terminal) when configured. When no +`core` has its own [logging system](./server/logging/README.mdx) and will output log records directly (e.g. to file or terminal) when configured. When no specific configuration is provided, logs are forwarded to the "legacy" Kibana so that they look the same as the rest of the log records throughout Kibana. From ef0931219fb13077fd37813dfe514c046de58ec5 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 3 Mar 2021 13:40:07 -0700 Subject: [PATCH 04/14] [dev/build_ts_refs] ignore type checking failures when building ts refs (#93473) Co-authored-by: spalger --- package.json | 2 +- src/dev/typescript/build_ts_refs_cli.ts | 30 +++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 6e5feac8a16a..9996b216b1a6 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "kbn:watch": "node scripts/kibana --dev --logging.json=false", "build:types": "rm -rf ./target/types && tsc --p tsconfig.types.json", "docs:acceptApiChanges": "node --max-old-space-size=6144 scripts/check_published_api_changes.js --accept", - "kbn:bootstrap": "node scripts/build_ts_refs", + "kbn:bootstrap": "node scripts/build_ts_refs --ignore-type-failures", "spec_to_console": "node scripts/spec_to_console", "backport-skip-ci": "backport --prDescription \"[skip-ci]\"", "storybook": "node scripts/storybook", diff --git a/src/dev/typescript/build_ts_refs_cli.ts b/src/dev/typescript/build_ts_refs_cli.ts index a073e5862327..63eb1a3da21e 100644 --- a/src/dev/typescript/build_ts_refs_cli.ts +++ b/src/dev/typescript/build_ts_refs_cli.ts @@ -18,6 +18,14 @@ import { concurrentMap } from './concurrent_map'; const CACHE_WORKING_DIR = Path.resolve(REPO_ROOT, 'data/ts_refs_output_cache'); +const TS_ERROR_REF = /\sTS\d{1,6}:\s/; + +const isTypeFailure = (error: any) => + error.exitCode === 1 && + error.stderr === '' && + typeof error.stdout === 'string' && + TS_ERROR_REF.test(error.stdout); + export async function runBuildRefsCli() { run( async ({ log, flags }) => { @@ -48,7 +56,20 @@ export async function runBuildRefsCli() { await outputCache.initCaches(); } - await buildAllTsRefs(log); + try { + await buildAllTsRefs(log); + log.success('ts refs build successfully'); + } catch (error) { + const typeFailure = isTypeFailure(error); + + if (flags['ignore-type-failures'] && typeFailure) { + log.warning( + 'tsc reported type errors but we are ignoring them for now, to see them please run `node scripts/type_check` or `node scripts/build_ts_refs` without the `--ignore-type-failures` flag.' + ); + } else { + throw error; + } + } if (outputCache && doCapture) { await outputCache.captureCache(Path.resolve(REPO_ROOT, 'target/ts_refs_cache')); @@ -61,10 +82,15 @@ export async function runBuildRefsCli() { { description: 'Build TypeScript projects', flags: { - boolean: ['clean', 'cache'], + boolean: ['clean', 'cache', 'ignore-type-failures'], default: { cache: true, }, + help: ` + --clean Delete outDirs for each ts project before building + --no-cache Disable fetching/extracting outDir caches based on the mergeBase with upstream + --ignore-type-failures If tsc reports type errors, ignore them and just log a small warning. + `, }, log: { defaultLevel: 'debug', From 60adc73afae2f693097e57e7d3e10f8ea0926ae0 Mon Sep 17 00:00:00 2001 From: Patrick Mueller Date: Wed, 3 Mar 2021 16:48:30 -0500 Subject: [PATCH 05/14] [alerting] adds doc on JSON-expanded action variables and task manager max_workers (#92720) resolves https://github.com/elastic/kibana/issues/90006 For task manager, adds a note about the fact that the max_workers will be limited to 100 starting in 8.0. Currently we allow any value (because we always have), but do print a "deprecation" warning that the limit cannot be exceeded starting in 8.0 For alerting, adds note about the JSON expansion of action variables which are objects. --- docs/settings/task-manager-settings.asciidoc | 2 +- docs/user/alerting/defining-alerts.asciidoc | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/settings/task-manager-settings.asciidoc b/docs/settings/task-manager-settings.asciidoc index 507e54349276..52878279ff06 100644 --- a/docs/settings/task-manager-settings.asciidoc +++ b/docs/settings/task-manager-settings.asciidoc @@ -27,6 +27,6 @@ Task Manager runs background tasks by polling for work on an interval. You can | `xpack.task_manager.max_workers` | The maximum number of tasks that this Kibana instance will run simultaneously. Defaults to 10. - + Starting in 8.0, it will not be possible to set the value greater than 100. |=== diff --git a/docs/user/alerting/defining-alerts.asciidoc b/docs/user/alerting/defining-alerts.asciidoc index 27f3a6c7309c..8f1a0f06f75a 100644 --- a/docs/user/alerting/defining-alerts.asciidoc +++ b/docs/user/alerting/defining-alerts.asciidoc @@ -95,6 +95,10 @@ Some cases exist where the variable values will be "escaped", when used in a con Mustache also supports "triple braces" of the form `{{{variable name}}}`, which indicates no escaping should be done at all. Care should be used when using this form, as it could end up rendering the variable content in such a way as to make the resulting parameter invalid or formatted incorrectly. +Each alert type defines additional variables as properties of the variable `context`. For example, if an alert type defines a variable `value`, it can be used in an action parameter as `{{context.value}}`. + +For diagnostic or exploratory purposes, action variables whose values are objects, such as `context`, can be referenced directly as variables. The resulting value will be a JSON representation of the object. For example, if an action parameter includes `{{context}}`, it will expand to the JSON representation of all the variables and values provided by the alert type. + You can attach more than one action. Clicking the "Add action" button will prompt you to select another alert type and repeat the above steps again. [role="screenshot"] From a61e1dd9fa9f34594b05a1a0c75358be6ab9c983 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet Date: Wed, 3 Mar 2021 17:04:52 -0500 Subject: [PATCH 06/14] [Fleet] Fix package version comparaison in the UI (#93498) --- .../fleet/sections/epm/screens/detail/index.tsx | 3 ++- .../fleet/sections/epm/screens/detail/settings/settings.tsx | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/epm/screens/detail/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/epm/screens/detail/index.tsx index 2d442744bb13..8b278204ca2c 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/epm/screens/detail/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/epm/screens/detail/index.tsx @@ -21,6 +21,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; +import semverLt from 'semver/functions/lt'; import { useUIExtension } from '../../../../hooks/use_ui_extension'; import { PAGE_ROUTING_PATHS, PLUGIN_ID } from '../../../../constants'; @@ -80,7 +81,7 @@ export function Detail() { packageInfo && 'savedObject' in packageInfo && packageInfo.savedObject && - packageInfo.savedObject.attributes.version < packageInfo.latestVersion; + semverLt(packageInfo.savedObject.attributes.version, packageInfo.latestVersion); // Fetch package info const { data: packageInfoData, error: packageInfoError, isLoading } = useGetPackageInfoByKey( diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/epm/screens/detail/settings/settings.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/epm/screens/detail/settings/settings.tsx index e69d148f89e0..94c03f76cdda 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/epm/screens/detail/settings/settings.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/epm/screens/detail/settings/settings.tsx @@ -8,6 +8,8 @@ import React, { memo } from 'react'; import styled from 'styled-components'; import { FormattedMessage } from '@kbn/i18n/react'; +import semverLt from 'semver/functions/lt'; + import { EuiTitle, EuiFlexGroup, EuiFlexItem, EuiText, EuiSpacer } from '@elastic/eui'; import { InstallStatus, PackageInfo } from '../../../../../types'; @@ -57,7 +59,9 @@ export const SettingsPage: React.FC = memo(({ packageInfo }: Props) => { }); const { status: installationStatus, version: installedVersion } = getPackageInstallStatus(name); const packageHasUsages = !!packagePoliciesData?.total; - const updateAvailable = installedVersion && installedVersion < latestVersion ? true : false; + const updateAvailable = + installedVersion && semverLt(installedVersion, latestVersion) ? true : false; + const isViewingOldPackage = version < latestVersion; // hide install/remove options if the user has version of the package is installed // and this package is out of date or if they do have a version installed but it's not this one From 4e21faeeb69c032cb72a031e16f8e4a7c778d5f5 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Wed, 3 Mar 2021 17:30:08 -0600 Subject: [PATCH 07/14] Fix service map for All environment single service (#93517) Before we removed environment from the UI filters (#89647), the environment query parameter would be undefined if "All" was selected. Now we send ENVIRONMENT_ALL in as the query parameter. Changes in https://github.com/elastic/kibana/blob/master/x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts made it so no connections would be returned if ENVIRONMENT_ALL was selected, rather than all connections. Since no connections were being returned, no elements except the selected service would be returned in the API response. This changes it so if ENVIRONMENT_ALL is selected, the connection will always be returned, just like what used to be the case when environment was undefined. Add an API test for this case. Fixes #93385. --- .../get_service_map_from_trace_ids.ts | 6 +- .../tests/service_maps/service_maps.ts | 188 ++++++++++-------- 2 files changed, 105 insertions(+), 89 deletions(-) diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts index 6e9225041b19..7e43c8f68437 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_from_trace_ids.ts @@ -30,10 +30,8 @@ export function getConnections({ if (!paths) { return []; } - const isEnvironmentSelected = - environment && environment !== ENVIRONMENT_ALL.value; - if (serviceName || isEnvironmentSelected) { + if (serviceName || environment) { paths = paths.filter((path) => { return ( path @@ -46,7 +44,7 @@ export function getConnections({ return false; } - if (!environment) { + if (!environment || environment === ENVIRONMENT_ALL.value) { return true; } diff --git a/x-pack/test/apm_api_integration/tests/service_maps/service_maps.ts b/x-pack/test/apm_api_integration/tests/service_maps/service_maps.ts index f452514cb517..a48811ad7024 100644 --- a/x-pack/test/apm_api_integration/tests/service_maps/service_maps.ts +++ b/x-pack/test/apm_api_integration/tests/service_maps/service_maps.ts @@ -6,6 +6,7 @@ */ import querystring from 'querystring'; +import url from 'url'; import expect from '@kbn/expect'; import { isEmpty, uniq } from 'lodash'; import archives_metadata from '../../common/fixtures/es_archiver/archives_metadata'; @@ -131,110 +132,127 @@ export default function serviceMapsApiTests({ getService }: FtrProviderContext) expect(environments.has(ENVIRONMENT_NOT_DEFINED)).to.eql(true); expectSnapshot(body).toMatch(); }); - }); - - describe('/api/apm/service-map with ML data', () => { - describe('with the default apm user', () => { - let response: PromiseReturnType; - before(async () => { - response = await supertest.get(`/api/apm/service-map?start=${start}&end=${end}`); - }); + describe('with ML data', () => { + describe('with the default apm user', () => { + before(async () => { + response = await supertest.get(`/api/apm/service-map?start=${start}&end=${end}`); + }); - it('returns service map elements with anomaly stats', () => { - expect(response.status).to.be(200); - const dataWithAnomalies = response.body.elements.filter( - (el: { data: { serviceAnomalyStats?: {} } }) => !isEmpty(el.data.serviceAnomalyStats) - ); + it('returns service map elements with anomaly stats', () => { + expect(response.status).to.be(200); + const dataWithAnomalies = response.body.elements.filter( + (el: { data: { serviceAnomalyStats?: {} } }) => !isEmpty(el.data.serviceAnomalyStats) + ); - expect(dataWithAnomalies).to.not.empty(); + expect(dataWithAnomalies).to.not.empty(); - dataWithAnomalies.forEach(({ data }: any) => { - expect( - Object.values(data.serviceAnomalyStats).filter((value) => isEmpty(value)) - ).to.not.empty(); + dataWithAnomalies.forEach(({ data }: any) => { + expect( + Object.values(data.serviceAnomalyStats).filter((value) => isEmpty(value)) + ).to.not.empty(); + }); }); - }); - it('returns the correct anomaly stats', () => { - const dataWithAnomalies = response.body.elements.filter( - (el: { data: { serviceAnomalyStats?: {} } }) => !isEmpty(el.data.serviceAnomalyStats) - ); - - expectSnapshot(dataWithAnomalies.length).toMatchInline(`8`); - expectSnapshot(dataWithAnomalies.slice(0, 3)).toMatchInline(` - Array [ - Object { - "data": Object { - "agent.name": "python", - "id": "opbeans-python", - "service.name": "opbeans-python", - "serviceAnomalyStats": Object { - "actualValue": 24282.2352941176, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-environment_not_defined-5626-high_mean_transaction_duration", - "serviceName": "opbeans-python", - "transactionType": "request", + it('returns the correct anomaly stats', () => { + const dataWithAnomalies = response.body.elements.filter( + (el: { data: { serviceAnomalyStats?: {} } }) => !isEmpty(el.data.serviceAnomalyStats) + ); + + expectSnapshot(dataWithAnomalies.length).toMatchInline(`8`); + expectSnapshot(dataWithAnomalies.slice(0, 3)).toMatchInline(` + Array [ + Object { + "data": Object { + "agent.name": "python", + "id": "opbeans-python", + "service.name": "opbeans-python", + "serviceAnomalyStats": Object { + "actualValue": 24282.2352941176, + "anomalyScore": 0, + "healthStatus": "healthy", + "jobId": "apm-environment_not_defined-5626-high_mean_transaction_duration", + "serviceName": "opbeans-python", + "transactionType": "request", + }, }, }, - }, - Object { - "data": Object { - "agent.name": "nodejs", - "id": "opbeans-node", - "service.environment": "testing", - "service.name": "opbeans-node", - "serviceAnomalyStats": Object { - "actualValue": 29300.5555555556, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-testing-384f-high_mean_transaction_duration", - "serviceName": "opbeans-node", - "transactionType": "request", + Object { + "data": Object { + "agent.name": "nodejs", + "id": "opbeans-node", + "service.environment": "testing", + "service.name": "opbeans-node", + "serviceAnomalyStats": Object { + "actualValue": 29300.5555555556, + "anomalyScore": 0, + "healthStatus": "healthy", + "jobId": "apm-testing-384f-high_mean_transaction_duration", + "serviceName": "opbeans-node", + "transactionType": "request", + }, }, }, - }, - Object { - "data": Object { - "agent.name": "rum-js", - "id": "opbeans-rum", - "service.environment": "testing", - "service.name": "opbeans-rum", - "serviceAnomalyStats": Object { - "actualValue": 2386500, - "anomalyScore": 0, - "healthStatus": "healthy", - "jobId": "apm-testing-384f-high_mean_transaction_duration", - "serviceName": "opbeans-rum", - "transactionType": "page-load", + Object { + "data": Object { + "agent.name": "rum-js", + "id": "opbeans-rum", + "service.environment": "testing", + "service.name": "opbeans-rum", + "serviceAnomalyStats": Object { + "actualValue": 2386500, + "anomalyScore": 0, + "healthStatus": "healthy", + "jobId": "apm-testing-384f-high_mean_transaction_duration", + "serviceName": "opbeans-rum", + "transactionType": "page-load", + }, }, }, - }, - ] - `); + ] + `); - expectSnapshot(response.body).toMatch(); + expectSnapshot(response.body).toMatch(); + }); }); - }); - describe('with a user that does not have access to ML', () => { - let response: PromiseReturnType; + describe('with a user that does not have access to ML', () => { + before(async () => { + response = await supertestAsApmReadUserWithoutMlAccess.get( + `/api/apm/service-map?start=${start}&end=${end}` + ); + }); - before(async () => { - response = await supertestAsApmReadUserWithoutMlAccess.get( - `/api/apm/service-map?start=${start}&end=${end}` - ); - }); + it('returns service map elements without anomaly stats', () => { + expect(response.status).to.be(200); - it('returns service map elements without anomaly stats', () => { - expect(response.status).to.be(200); + const dataWithAnomalies = response.body.elements.filter( + (el: { data: { serviceAnomalyStats?: {} } }) => !isEmpty(el.data.serviceAnomalyStats) + ); - const dataWithAnomalies = response.body.elements.filter( - (el: { data: { serviceAnomalyStats?: {} } }) => !isEmpty(el.data.serviceAnomalyStats) - ); + expect(dataWithAnomalies).to.be.empty(); + }); + }); + }); + + describe('with a single service', () => { + describe('when ENVIRONMENT_ALL is selected', () => { + it('returns service map elements', async () => { + response = await supertest.get( + url.format({ + pathname: '/api/apm/service-map', + query: { + environment: 'ENVIRONMENT_ALL', + start: metadata.start, + end: metadata.end, + serviceName: 'opbeans-java', + }, + }) + ); - expect(dataWithAnomalies).to.be.empty(); + expect(response.status).to.be(200); + expect(response.body.elements.length).to.be.greaterThan(1); + }); }); }); }); From a953e90dd48fee3146aef7552af8a104fffdec9a Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Thu, 4 Mar 2021 00:27:29 +0000 Subject: [PATCH 08/14] [Security Solution] fix data provider cypress test (#93465) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary This PR is to fix data_provider's cypress test: displays the data provider action menu when Enter is pressed. When I ran it locally, I couldn’t reproduce it every time. There’s a chance that the timeline was opened but the filter we put wasn’t there, this happen when I simulate slow 3G with Chrome or once out of my 10-time-trial with `loop_cypress_tests.js` [failure 1](https://kibana-ci.elastic.co/job/elastic+kibana+security-cypress/4313/testReport/junit/(root)/displays%20the%20data%20provider%20action%20menu%20when%20Enter%20is%20pressed/timeline_data_providers_displays_the_data_provider_action_menu_when_Enter_is_pressed/) [failure 2](https://kibana-ci.elastic.co/job/elastic+kibana+security-cypress/4313/testReport/junit/(root)/displays%20the%20data%20provider%20action%20menu%20when%20Enter%20is%20pressed/timeline_data_providers_displays_the_data_provider_action_menu_when_Enter_is_pressed_2/) How to run this test several times automatically: 1. Go to the file and mark your case with .only 2. Copy the relative path of the file 3. Go to x-pack/plugins/security_solution/package.json Line 13, change —spec to the path you just copied (e.g: ./cypress/integration/timelines/data_providers.spec.ts) 4. Go to Kibana/ and run node x-pack/plugins/security_solution/scripts/loop_cypress_tests.js 1 (1 means run it once, you can put more to check flakiness) 5. After it finishes, it generates you a report under: kibana/target/loop-cypress-tests.txt 6. Search for `displays the data provider action menu when Enter is pressed.` and see if all passes. --- .../integration/timelines/data_providers.spec.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/timelines/data_providers.spec.ts b/x-pack/plugins/security_solution/cypress/integration/timelines/data_providers.spec.ts index 3505a9b6a791..7d72747c8493 100644 --- a/x-pack/plugins/security_solution/cypress/integration/timelines/data_providers.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/timelines/data_providers.spec.ts @@ -10,6 +10,7 @@ import { TIMELINE_DATA_PROVIDERS_EMPTY, TIMELINE_DROPPED_DATA_PROVIDERS, TIMELINE_DATA_PROVIDERS_ACTION_MENU, + TIMELINE_FLYOUT_HEADER, } from '../../screens/timeline'; import { HOSTS_NAMES_DRAGGABLE } from '../../screens/hosts/all_hosts'; @@ -60,8 +61,13 @@ describe('timeline data providers', () => { openTimelineUsingToggle(); cy.get(TIMELINE_DATA_PROVIDERS_ACTION_MENU).should('not.exist'); - cy.get(TIMELINE_DROPPED_DATA_PROVIDERS).first().focus(); - cy.get(TIMELINE_DROPPED_DATA_PROVIDERS).first().parent().type('{enter}'); + cy.get(`${TIMELINE_FLYOUT_HEADER} ${TIMELINE_DROPPED_DATA_PROVIDERS}`) + .pipe(($el) => $el.trigger('focus')) + .should('exist'); + cy.get(`${TIMELINE_FLYOUT_HEADER} ${TIMELINE_DROPPED_DATA_PROVIDERS}`) + .first() + .parent() + .type('{enter}'); cy.get(TIMELINE_DATA_PROVIDERS_ACTION_MENU).should('exist'); }); From 3ba17f284e20970fcefceac61059ca7109a2a8d6 Mon Sep 17 00:00:00 2001 From: Spencer Date: Wed, 3 Mar 2021 17:47:52 -0700 Subject: [PATCH 09/14] [dev/build_ts_refs] support disabling the ts-refs build completely (#93529) Co-authored-by: spalger --- src/dev/typescript/build_ts_refs_cli.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/dev/typescript/build_ts_refs_cli.ts b/src/dev/typescript/build_ts_refs_cli.ts index 63eb1a3da21e..42b278ce6450 100644 --- a/src/dev/typescript/build_ts_refs_cli.ts +++ b/src/dev/typescript/build_ts_refs_cli.ts @@ -29,6 +29,13 @@ const isTypeFailure = (error: any) => export async function runBuildRefsCli() { run( async ({ log, flags }) => { + if (process.env.BUILD_TS_REFS_DISABLE === 'true' && !flags.force) { + log.info( + 'Building ts refs is disabled because the BUILD_TS_REFS_DISABLE environment variable is set to "true". Pass `--force` to run the build anyway.' + ); + return; + } + const outDirs = getOutputsDeep(REF_CONFIG_PATHS); const cacheEnabled = process.env.BUILD_TS_REFS_CACHE_ENABLE !== 'false' && !!flags.cache; @@ -82,11 +89,12 @@ export async function runBuildRefsCli() { { description: 'Build TypeScript projects', flags: { - boolean: ['clean', 'cache', 'ignore-type-failures'], + boolean: ['clean', 'force', 'cache', 'ignore-type-failures'], default: { cache: true, }, help: ` + --force Run the build even if the BUILD_TS_REFS_DISABLE is set to "true" --clean Delete outDirs for each ts project before building --no-cache Disable fetching/extracting outDir caches based on the mergeBase with upstream --ignore-type-failures If tsc reports type errors, ignore them and just log a small warning. From 40a33865532667b0c16c9ae210d557e45eb49577 Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Wed, 3 Mar 2021 19:56:37 -0500 Subject: [PATCH 10/14] Bump dependencies (#93511) --- package.json | 2 +- yarn.lock | 66 ++++++++++++++++++++++++++-------------------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/package.json b/package.json index 9996b216b1a6..0baafd101dfb 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "**/load-grunt-config/lodash": "^4.17.21", "**/minimist": "^1.2.5", "**/node-jose/node-forge": "^0.10.0", - "**/prismjs": "1.22.0", + "**/prismjs": "1.23.0", "**/react-syntax-highlighter": "^15.3.1", "**/react-syntax-highlighter/**/highlight.js": "^10.4.1", "**/request": "^2.88.2", diff --git a/yarn.lock b/yarn.lock index bcf5c37f6f91..efe77edbaed8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8443,10 +8443,10 @@ bmp-js@^0.1.0: resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.1.0.tgz#e05a63f796a6c1ff25f4771ec7adadc148c07233" integrity sha1-4Fpj95amwf8l9Hcex62twUjAcjM= -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: - version "4.11.8" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" - integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.9: + version "4.11.9" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828" + integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw== body-parser@1.19.0, body-parser@^1.18.1, body-parser@^1.18.3: version "1.19.0" @@ -8586,7 +8586,7 @@ broadcast-channel@^3.0.3: rimraf "3.0.0" unload "2.2.0" -brorand@^1.0.1: +brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= @@ -12353,17 +12353,17 @@ element-resize-detector@^1.1.15: batch-processor "^1.0.0" elliptic@^6.0.0: - version "6.5.3" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" - integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw== + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== dependencies: - bn.js "^4.4.0" - brorand "^1.0.1" + bn.js "^4.11.9" + brorand "^1.1.0" hash.js "^1.0.0" - hmac-drbg "^1.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" emittery@^0.7.1: version "0.7.1" @@ -15796,7 +15796,7 @@ hjson@3.2.1: resolved "https://registry.yarnpkg.com/hjson/-/hjson-3.2.1.tgz#20de41dc87fc9a10d1557d0230b0e02afb1b09ac" integrity sha512-OhhrFMeC7dVuA1xvxuXGTv/yTdhTvbe8hz+3LgVNsfi9+vgz0sF/RrkuX8eegpKaMc9cwYwydImBH6iePoJtdQ== -hmac-drbg@^1.0.0: +hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= @@ -20018,12 +20018,12 @@ mini-css-extract-plugin@0.8.0: schema-utils "^1.0.0" webpack-sources "^1.1.0" -minimalistic-assert@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" - integrity sha1-cCvi3aazf0g2vLP121ZkG2Sh09M= +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: +minimalistic-crypto-utils@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= @@ -20811,9 +20811,9 @@ node-modules-regexp@^1.0.0: integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= node-notifier@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.0.tgz#a7eee2d51da6d0f7ff5094bc7108c911240c1620" - integrity sha512-46z7DUmcjoYdaWyXouuFNNfUo6eFa94t23c53c+lG/9Cvauk4a98rAUp9672X5dxGdQmLpPzTxzu8f/OeEPaFA== + version "8.0.1" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.1.tgz#f86e89bbc925f2b068784b31f382afdc6ca56be1" + integrity sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA== dependencies: growly "^1.3.0" is-wsl "^2.2.0" @@ -22627,10 +22627,10 @@ printj@~1.1.0: resolved "https://registry.yarnpkg.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== -prismjs@1.22.0, prismjs@^1.22.0, prismjs@~1.22.0: - version "1.22.0" - resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.22.0.tgz#73c3400afc58a823dd7eed023f8e1ce9fd8977fa" - integrity sha512-lLJ/Wt9yy0AiSYBf212kK3mM5L8ycwlyTlSxHBAneXLR0nzFMlZ5y7riFPF3E33zXOF2IH95xdY5jIyZbM9z/w== +prismjs@1.23.0, prismjs@^1.22.0, prismjs@~1.22.0: + version "1.23.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.23.0.tgz#d3b3967f7d72440690497652a9d40ff046067f33" + integrity sha512-c29LVsqOaLbBHuIbsTxaKENh1N2EQBOHaWv7gkHN4dgRbxSREqDnDbtFJYdpPauS4YCplMSNCABQ6Eeor69bAA== optionalDependencies: clipboard "^2.0.0" @@ -27907,9 +27907,9 @@ typescript@4.1.3, typescript@^3.2.2, typescript@^3.3.3333, typescript@^3.5.3, ty integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== ua-parser-js@^0.7.18: - version "0.7.23" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.23.tgz#704d67f951e13195fbcd3d78818577f5bc1d547b" - integrity sha512-m4hvMLxgGHXG3O3fQVAyyAQpZzDOvwnhOTjYz5Xmr7r/+LpkNy3vJXdVRWgd1TkAb7NGROZuSy96CrlNVjA7KA== + version "0.7.24" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.24.tgz#8d3ecea46ed4f1f1d63ec25f17d8568105dc027c" + integrity sha512-yo+miGzQx5gakzVK3QFfN0/L9uVhosXBBO7qmnk7c2iw1IhL212wfA3zbnI54B0obGwC/5NWub/iT9sReMx+Fw== uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.5" @@ -28492,9 +28492,9 @@ url-parse-lax@^3.0.0: prepend-http "^2.0.0" url-parse@^1.4.3, url-parse@^1.4.7: - version "1.4.7" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" - integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== + version "1.5.1" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.1.tgz#d5fa9890af8a5e1f274a2c98376510f6425f6e3b" + integrity sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q== dependencies: querystringify "^2.1.1" requires-port "^1.0.0" From 2a8e4b260f104192a77c2f94103b54be17bfc3f5 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 4 Mar 2021 01:16:37 +0000 Subject: [PATCH 11/14] chore(NA): do not use execa on bazel workspace status update script (#93532) --- src/dev/bazel_workspace_status.js | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/dev/bazel_workspace_status.js b/src/dev/bazel_workspace_status.js index 3c3ef1574cd8..c7ae05ce4874 100644 --- a/src/dev/bazel_workspace_status.js +++ b/src/dev/bazel_workspace_status.js @@ -17,13 +17,21 @@ // If the script exits with non-zero code, it's considered as a failure // and the output will be discarded. -(async () => { - const execa = require('execa'); +(() => { + const cp = require('child_process'); const os = require('os'); - async function runCmd(cmd, args) { + function runCmd(cmd, args) { try { - return await execa(cmd, args); + const spawnResult = cp.spawnSync(cmd, args); + const exitCode = spawnResult.status !== null ? spawnResult.status : 1; + const stdoutStr = spawnResult.stdout.toString(); + const stdout = stdoutStr ? stdoutStr.trim() : null; + + return { + exitCode, + stdout, + }; } catch (e) { return { exitCode: 1 }; } @@ -31,29 +39,25 @@ // Git repo const kbnGitOriginName = process.env.KBN_GIT_ORIGIN_NAME || 'origin'; - const repoUrlCmdResult = await runCmd('git', [ - 'config', - '--get', - `remote.${kbnGitOriginName}.url`, - ]); + const repoUrlCmdResult = runCmd('git', ['config', '--get', `remote.${kbnGitOriginName}.url`]); if (repoUrlCmdResult.exitCode === 0) { // Only output REPO_URL when found it console.log(`REPO_URL ${repoUrlCmdResult.stdout}`); } // Commit SHA - const commitSHACmdResult = await runCmd('git', ['rev-parse', 'HEAD']); + const commitSHACmdResult = runCmd('git', ['rev-parse', 'HEAD']); if (commitSHACmdResult.exitCode === 0) { console.log(`COMMIT_SHA ${commitSHACmdResult.stdout}`); // Branch - const gitBranchCmdResult = await runCmd('git', ['rev-parse', '--abbrev-ref', 'HEAD']); + const gitBranchCmdResult = runCmd('git', ['rev-parse', '--abbrev-ref', 'HEAD']); if (gitBranchCmdResult.exitCode === 0) { console.log(`GIT_BRANCH ${gitBranchCmdResult.stdout}`); } // Tree status - const treeStatusCmdResult = await runCmd('git', ['diff-index', '--quiet', 'HEAD', '--']); + const treeStatusCmdResult = runCmd('git', ['diff-index', '--quiet', 'HEAD', '--']); const treeStatusVarStr = 'GIT_TREE_STATUS'; if (treeStatusCmdResult.exitCode === 0) { console.log(`${treeStatusVarStr} Clean`); @@ -64,7 +68,7 @@ // Host if (process.env.CI) { - const hostCmdResult = await runCmd('hostname'); + const hostCmdResult = runCmd('hostname'); const hostStr = hostCmdResult.stdout.split('-').slice(0, -1).join('-'); const coresStr = os.cpus().filter((cpu, index) => { return !cpu.model.includes('Intel') || index % 2 === 1; From 3316fb43fdcf69b3d8f7538c51b8756392550a4c Mon Sep 17 00:00:00 2001 From: Davis Plumlee <56367316+dplumlee@users.noreply.github.com> Date: Wed, 3 Mar 2021 21:22:05 -0500 Subject: [PATCH 12/14] [Security Solution][Detections] ML Popover overflow fix (#93525) Co-authored-by: Garrett Spong --- .../public/common/components/ml_popover/ml_popover.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/ml_popover.tsx b/x-pack/plugins/security_solution/public/common/components/ml_popover/ml_popover.tsx index 1f216bb8da1a..561805217e8a 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/ml_popover.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/ml_popover.tsx @@ -27,6 +27,10 @@ import { useSecurityJobs } from './hooks/use_security_jobs'; const PopoverContentsDiv = styled.div` max-width: 684px; + max-height: 90vh; + overflow-y: auto; + overflow-x: hidden; + padding-bottom: 15px; `; PopoverContentsDiv.displayName = 'PopoverContentsDiv'; From 89373490d0dcdc5dd51c33cb4a85b03ebeaf7a86 Mon Sep 17 00:00:00 2001 From: Kevin Qualters <56408403+kqualters-elastic@users.noreply.github.com> Date: Thu, 4 Mar 2021 00:24:58 -0500 Subject: [PATCH 13/14] Display multiple copyable fields for process.args in resolver node detail panel (#93280) --- .../common/endpoint/models/event.ts | 4 ++-- .../public/resolver/mocks/endpoint_event.ts | 2 +- .../public/resolver/view/panel.test.tsx | 19 +++++++++++---- .../resolver/view/panels/node_detail.tsx | 24 ++++++++++++++++--- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/models/event.ts b/x-pack/plugins/security_solution/common/endpoint/models/event.ts index 692c1d3757b8..9c102e77b694 100644 --- a/x-pack/plugins/security_solution/common/endpoint/models/event.ts +++ b/x-pack/plugins/security_solution/common/endpoint/models/event.ts @@ -160,12 +160,12 @@ export function md5HashForProcess(event: SafeResolverEvent): string | undefined /** * First non-null value for the `event.process.args` field. */ -export function argsForProcess(event: SafeResolverEvent): string | undefined { +export function argsForProcess(event: SafeResolverEvent): string[] | undefined { if (isLegacyEventSafeVersion(event)) { // There is not currently a key for this on Legacy event types return undefined; } - return firstNonNullValue(event.process?.args); + return values(event.process?.args); } /** diff --git a/x-pack/plugins/security_solution/public/resolver/mocks/endpoint_event.ts b/x-pack/plugins/security_solution/public/resolver/mocks/endpoint_event.ts index 04541189683a..59089af4a22f 100644 --- a/x-pack/plugins/security_solution/public/resolver/mocks/endpoint_event.ts +++ b/x-pack/plugins/security_solution/public/resolver/mocks/endpoint_event.ts @@ -51,7 +51,7 @@ export function mockEndpointEvent({ process: { entity_id: entityID, executable: 'executable', - args: 'args', + args: ['args0', 'args1', 'args2'], name: processName, pid, hash: { diff --git a/x-pack/plugins/security_solution/public/resolver/view/panel.test.tsx b/x-pack/plugins/security_solution/public/resolver/view/panel.test.tsx index eeb9d65cd6ac..332f806b59ec 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panel.test.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panel.test.tsx @@ -34,7 +34,7 @@ describe(`Resolver: when analyzing a tree with no ancestors and two children and /** * These are the details we expect to see in the node detail view when the origin is selected. */ - const originEventDetailEntries: ReadonlyMap = new Map([ + const originEventDetailEntries: Array<[string, string]> = [ ['@timestamp', 'Sep 23, 2020 @ 08:25:32.316'], ['process.executable', 'executable'], ['process.pid', '0'], @@ -42,8 +42,10 @@ describe(`Resolver: when analyzing a tree with no ancestors and two children and ['user.domain', 'user.domain'], ['process.parent.pid', '0'], ['process.hash.md5', 'hash.md5'], - ['process.args', 'args'], - ]); + ['process.args', 'args0'], + ['process.args', 'args1'], + ['process.args', 'args2'], + ]; beforeEach(() => { // create a mock data access layer @@ -129,11 +131,16 @@ describe(`Resolver: when analyzing a tree with no ancestors and two children and describe.each([...originEventDetailEntries])( 'when the user hovers over the description for the field (%p) with their mouse', (fieldTitleText, value) => { + // If there are multiple values for a field, i.e. an array, this is the index for the value we are testing. + const entryIndex = originEventDetailEntries + .filter(([fieldName]) => fieldName === fieldTitleText) + .findIndex(([_, fieldValue]) => fieldValue === value); beforeEach(async () => { const dt = await simulator().resolveWrapper(() => { return simulator() .testSubject('resolver:node-detail:entry-title') - .filterWhere((title) => title.text() === fieldTitleText); + .filterWhere((title) => title.text() === fieldTitleText) + .at(entryIndex); }); expect(dt).toHaveLength(1); @@ -184,7 +191,9 @@ describe(`Resolver: when analyzing a tree with no ancestors and two children and ['user.domain', 'user.domain'], ['process.parent.pid', '0'], ['process.hash.md5', 'hash.md5'], - ['process.args', 'args'], + ['process.args', 'args0'], + ['process.args', 'args1'], + ['process.args', 'args2'], ]); }); }); diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/node_detail.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/node_detail.tsx index da52994c4e71..ed3507d8f4bc 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/node_detail.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/node_detail.tsx @@ -122,8 +122,12 @@ const NodeDetailView = memo(function ({ description: eventModel.argsForProcess(processEvent), }; - // This is the data in {title, description} form for the EuiDescriptionList to display - const processDescriptionListData = [ + const flattenedEntries: Array<{ + title: string; + description: string | string[] | number | undefined; + }> = []; + + const flattenedDescriptionListData = [ createdEntry, pathEntry, pidEntry, @@ -132,7 +136,21 @@ const NodeDetailView = memo(function ({ parentPidEntry, md5Entry, commandLineEntry, - ] + ].reduce((flattenedList, entry) => { + if (Array.isArray(entry.description)) { + return [ + ...flattenedList, + ...entry.description.map((value) => { + return { title: entry.title, description: value }; + }), + ]; + } else { + return [...flattenedList, entry]; + } + }, flattenedEntries); + + // This is the data in {title, description} form for the EuiDescriptionList to display + const processDescriptionListData = flattenedDescriptionListData .filter((entry) => { return entry.description !== undefined; }) From 284a77c7c00e0855adeb31afb5287360d38f7562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Zolt=C3=A1n=20Szab=C3=B3?= Date: Thu, 4 Mar 2021 09:09:43 +0100 Subject: [PATCH 14/14] Reviews data frame analytics UI text (#93033) Co-authored-by: Lisa Cawley --- .../components/advanced_step/advanced_step_form.tsx | 2 +- .../components/advanced_step/outlier_hyper_parameters.tsx | 6 +++--- .../configuration_step/configuration_step_form.tsx | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/advanced_step_form.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/advanced_step_form.tsx index 71770dcf952d..21243351dab7 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/advanced_step_form.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/advanced_step_form.tsx @@ -560,7 +560,7 @@ export const AdvancedStepForm: FC = ({ })} helpText={i18n.translate('xpack.ml.dataframe.analytics.create.maxNumThreadsHelpText', { defaultMessage: - 'The maximum number of threads to be used by the analysis. The default value is 1', + 'The maximum number of threads to be used by the analysis. The default value is 1.', })} isInvalid={maxNumThreads === 0} error={ diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/outlier_hyper_parameters.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/outlier_hyper_parameters.tsx index d591c8c60275..d347a8147469 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/outlier_hyper_parameters.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/advanced_step/outlier_hyper_parameters.tsx @@ -30,7 +30,7 @@ export const OutlierHyperParameters: FC = ({ actions, state, advancedPara })} helpText={i18n.translate('xpack.ml.dataframe.analytics.create.methodHelpText', { defaultMessage: - 'Sets the method that outlier detection uses. If not set, uses an ensemble of different methods and normalises and combines their individual outlier scores to obtain the overall outlier score. We recommend to use the ensemble method', + 'Sets the method that outlier detection uses. If not set, uses an ensemble of different methods, normalizes and combines their individual outlier scores to obtain the overall outlier score. It is recommended to use the ensemble method.', })} isInvalid={advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.METHOD] !== undefined} error={advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.METHOD]} @@ -56,7 +56,7 @@ export const OutlierHyperParameters: FC = ({ actions, state, advancedPara })} helpText={i18n.translate('xpack.ml.dataframe.analytics.create.nNeighborsHelpText', { defaultMessage: - 'The value for how many nearest neighbors each method of outlier detection will use to calculate its outlier score. When not set, different values will be used for different ensemble members. Must be a positive integer', + 'The value for how many nearest neighbors each method of outlier detection uses to calculate its outlier score. When not set, different values are used for different ensemble members. Must be a positive integer.', })} isInvalid={advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.N_NEIGHBORS] !== undefined} error={advancedParamErrors[ANALYSIS_ADVANCED_FIELDS.N_NEIGHBORS]} @@ -66,7 +66,7 @@ export const OutlierHyperParameters: FC = ({ actions, state, advancedPara 'xpack.ml.dataframe.analytics.create.nNeighborsInputAriaLabel', { defaultMessage: - 'The value for how many nearest neighbors each method of outlier detection will use to calculate its outlier score.', + 'The value for how many nearest neighbors each method of outlier detection uses to calculate its outlier score.', } )} data-test-subj="mlAnalyticsCreateJobWizardnNeighborsInput" diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx index aa008192bbce..206fb511a4d2 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_creation/components/configuration_step/configuration_step_form.tsx @@ -539,7 +539,7 @@ export const ConfigurationStepForm: FC = ({ 'xpack.ml.dataframe.analytics.create.scatterplotMatrixLabelHelpText', { defaultMessage: - 'Visualizes the relationships between pairs of selected included fields', + 'Visualizes the relationships between pairs of selected included fields.', } )} fullWidth