diff --git a/docs/CHANGELOG.asciidoc b/docs/CHANGELOG.asciidoc index 1b45c8700d9a3..85c5bcfbf1127 100644 --- a/docs/CHANGELOG.asciidoc +++ b/docs/CHANGELOG.asciidoc @@ -93,37 +93,36 @@ The 8.16.0 release includes the following known issues. [discrete] [[known-199902]] -.Stack Monitoring shows "Unable to load page error" +.Stack Monitoring shows "Unable to load page" error [%collapsible] ==== *Details* + -The Stack Monitoring pages Overview, Nodes, Logs can stop working with the error "Unable to load page error". The Stack trace mentions `TypeError: Cannot read properties of undefined (reading 'logsLocator')`. +The Overview, Nodes, and Logs pages in Stack Monitoring show an "Unable to load page" error. The Stack trace mentions `TypeError: Cannot read properties of undefined (reading 'logsLocator')`. *Workaround* + -Disabling the `Set feature visibility > Logs` feature at Kibana Space settings level will prevent the error to occur. Please note the `Logs` feature will not be available on such space. +Disabling the `Set feature visibility > Logs` feature at the Kibana Space settings level will prevent the error from occurring. Please note the `Logs` feature will not be available on those spaces. -It's also possible to `Observability > Logs` feature privilege to `None` on the role level - this will hide the `Logs` feature for individual users and prevent the error for these users as well. +It's also possible to set the `Observability > Logs` feature privilege to `None` at the role level. This will hide the `Logs` feature from individual users and prevent the error for these users as well. For more information, refer to {kibana-issue}199902[#199902]. ==== [discrete] [[known-199891-199892]] -.Onboarding, tutorial of APM and OpenTelemetry and some "Beats Only" integrations will show the error "Unable to load page error" +.Onboarding, tutorial of APM and OpenTelemetry and some "Beats Only" integrations shows "Unable to load page" error [%collapsible] ==== *Details* + -Tutorials linked from the {kib} home page show an error "Unable to load page error". The Stack trace mentions `The above error occurred in tutorial_TutorialUi`. +Tutorials linked from the {kib} home page show an "Unable to load page" error. The Stack trace mentions `The above error occurred in tutorial_TutorialUi`. *Workaround* + -The APM / OpenTelemetry tutorials represented a shortcut to get important parameters to use in the configuration files quickly. -It is still possible to obtain the same parameters following the documentation tutorials of APM. +The APM / OpenTelemetry tutorials represented a shortcut to quickly add important parameters to the configuration files. +It is still possible to obtain the same parameters following the tutorials in the APM documentation. More information can be found in the {observability-guide}/apm-collect-application-data.html[APM documentation] and the {observability-guide}/get-started-with-fleet-apm-server.html[Fleet documentation]. For information about how to create APM API keys, please check the {observability-guide}/apm-api-key.html#apm-create-an-api-key[API key documentation]. - For more information, refer to {kibana-issue}199891[#199891] and {kibana-issue}199892[#199892]. ==== diff --git a/oas_docs/bundle.json b/oas_docs/bundle.json index a4c1e30c8cf05..d30ac3c4552e2 100644 --- a/oas_docs/bundle.json +++ b/oas_docs/bundle.json @@ -21335,7 +21335,7 @@ ] } }, - "/api/fleet/epm/packages/{pkgName}/{pkgVersion}/{filePath*}": { + "/api/fleet/epm/packages/{pkgName}/{pkgVersion}/{filePath}": { "get": { "operationId": "get-fleet-epm-packages-pkgname-pkgversion-filepath", "parameters": [ diff --git a/oas_docs/bundle.serverless.json b/oas_docs/bundle.serverless.json index a0bd0c4cc4340..4b56e3581c66f 100644 --- a/oas_docs/bundle.serverless.json +++ b/oas_docs/bundle.serverless.json @@ -21335,7 +21335,7 @@ ] } }, - "/api/fleet/epm/packages/{pkgName}/{pkgVersion}/{filePath*}": { + "/api/fleet/epm/packages/{pkgName}/{pkgVersion}/{filePath}": { "get": { "operationId": "get-fleet-epm-packages-pkgname-pkgversion-filepath", "parameters": [ diff --git a/oas_docs/output/kibana.serverless.yaml b/oas_docs/output/kibana.serverless.yaml index 117e52586c5ad..55dd5277d1d93 100644 --- a/oas_docs/output/kibana.serverless.yaml +++ b/oas_docs/output/kibana.serverless.yaml @@ -1018,24 +1018,17 @@ paths: - last_execution_date flapping: additionalProperties: false - description: >- - When flapping detection is turned on, alerts that switch - quickly between active and recovered states are identified - as “flapping” and notifications are reduced. + description: When flapping detection is turned on, alerts that switch quickly between active and recovered states are identified as “flapping” and notifications are reduced. nullable: true type: object properties: look_back_window: - description: >- - The minimum number of runs in which the threshold must - be met. + description: The minimum number of runs in which the threshold must be met. maximum: 20 minimum: 2 type: number status_change_threshold: - description: >- - The minimum number of times an alert must switch - states in the look back window. + description: The minimum number of times an alert must switch states in the look back window. maximum: 20 minimum: 2 type: number @@ -1610,24 +1603,17 @@ paths: type: boolean flapping: additionalProperties: false - description: >- - When flapping detection is turned on, alerts that switch - quickly between active and recovered states are identified - as “flapping” and notifications are reduced. + description: When flapping detection is turned on, alerts that switch quickly between active and recovered states are identified as “flapping” and notifications are reduced. nullable: true type: object properties: look_back_window: - description: >- - The minimum number of runs in which the threshold must - be met. + description: The minimum number of runs in which the threshold must be met. maximum: 20 minimum: 2 type: number status_change_threshold: - description: >- - The minimum number of times an alert must switch states - in the look back window. + description: The minimum number of times an alert must switch states in the look back window. maximum: 20 minimum: 2 type: number @@ -1945,24 +1931,17 @@ paths: - last_execution_date flapping: additionalProperties: false - description: >- - When flapping detection is turned on, alerts that switch - quickly between active and recovered states are identified - as “flapping” and notifications are reduced. + description: When flapping detection is turned on, alerts that switch quickly between active and recovered states are identified as “flapping” and notifications are reduced. nullable: true type: object properties: look_back_window: - description: >- - The minimum number of runs in which the threshold must - be met. + description: The minimum number of runs in which the threshold must be met. maximum: 20 minimum: 2 type: number status_change_threshold: - description: >- - The minimum number of times an alert must switch - states in the look back window. + description: The minimum number of times an alert must switch states in the look back window. maximum: 20 minimum: 2 type: number @@ -2540,24 +2519,17 @@ paths: - active flapping: additionalProperties: false - description: >- - When flapping detection is turned on, alerts that switch - quickly between active and recovered states are identified - as “flapping” and notifications are reduced. + description: When flapping detection is turned on, alerts that switch quickly between active and recovered states are identified as “flapping” and notifications are reduced. nullable: true type: object properties: look_back_window: - description: >- - The minimum number of runs in which the threshold must - be met. + description: The minimum number of runs in which the threshold must be met. maximum: 20 minimum: 2 type: number status_change_threshold: - description: >- - The minimum number of times an alert must switch states - in the look back window. + description: The minimum number of times an alert must switch states in the look back window. maximum: 20 minimum: 2 type: number @@ -2847,24 +2819,17 @@ paths: - last_execution_date flapping: additionalProperties: false - description: >- - When flapping detection is turned on, alerts that switch - quickly between active and recovered states are identified - as “flapping” and notifications are reduced. + description: When flapping detection is turned on, alerts that switch quickly between active and recovered states are identified as “flapping” and notifications are reduced. nullable: true type: object properties: look_back_window: - description: >- - The minimum number of runs in which the threshold must - be met. + description: The minimum number of runs in which the threshold must be met. maximum: 20 minimum: 2 type: number status_change_threshold: - description: >- - The minimum number of times an alert must switch - states in the look back window. + description: The minimum number of times an alert must switch states in the look back window. maximum: 20 minimum: 2 type: number @@ -3902,24 +3867,17 @@ paths: - last_execution_date flapping: additionalProperties: false - description: >- - When flapping detection is turned on, alerts that switch - quickly between active and recovered states are identified - as “flapping” and notifications are reduced. + description: When flapping detection is turned on, alerts that switch quickly between active and recovered states are identified as “flapping” and notifications are reduced. nullable: true type: object properties: look_back_window: - description: >- - The minimum number of runs in which the threshold must - be met. + description: The minimum number of runs in which the threshold must be met. maximum: 20 minimum: 2 type: number status_change_threshold: - description: >- - The minimum number of times an alert must switch - states in the look back window. + description: The minimum number of times an alert must switch states in the look back window. maximum: 20 minimum: 2 type: number @@ -19365,7 +19323,7 @@ paths: tags: - Elastic Package Manager (EPM) x-beta: true - /api/fleet/epm/packages/{pkgName}/{pkgVersion}/{filePath*}: + /api/fleet/epm/packages/{pkgName}/{pkgVersion}/{filePath}: get: operationId: get-fleet-epm-packages-pkgname-pkgversion-filepath parameters: diff --git a/oas_docs/output/kibana.yaml b/oas_docs/output/kibana.yaml index ceefaa13fcd4b..b2c3ae00be9d0 100644 --- a/oas_docs/output/kibana.yaml +++ b/oas_docs/output/kibana.yaml @@ -1367,24 +1367,17 @@ paths: - last_execution_date flapping: additionalProperties: false - description: >- - When flapping detection is turned on, alerts that switch - quickly between active and recovered states are identified - as “flapping” and notifications are reduced. + description: When flapping detection is turned on, alerts that switch quickly between active and recovered states are identified as “flapping” and notifications are reduced. nullable: true type: object properties: look_back_window: - description: >- - The minimum number of runs in which the threshold must - be met. + description: The minimum number of runs in which the threshold must be met. maximum: 20 minimum: 2 type: number status_change_threshold: - description: >- - The minimum number of times an alert must switch - states in the look back window. + description: The minimum number of times an alert must switch states in the look back window. maximum: 20 minimum: 2 type: number @@ -1958,24 +1951,17 @@ paths: type: boolean flapping: additionalProperties: false - description: >- - When flapping detection is turned on, alerts that switch - quickly between active and recovered states are identified - as “flapping” and notifications are reduced. + description: When flapping detection is turned on, alerts that switch quickly between active and recovered states are identified as “flapping” and notifications are reduced. nullable: true type: object properties: look_back_window: - description: >- - The minimum number of runs in which the threshold must - be met. + description: The minimum number of runs in which the threshold must be met. maximum: 20 minimum: 2 type: number status_change_threshold: - description: >- - The minimum number of times an alert must switch states - in the look back window. + description: The minimum number of times an alert must switch states in the look back window. maximum: 20 minimum: 2 type: number @@ -2293,24 +2279,17 @@ paths: - last_execution_date flapping: additionalProperties: false - description: >- - When flapping detection is turned on, alerts that switch - quickly between active and recovered states are identified - as “flapping” and notifications are reduced. + description: When flapping detection is turned on, alerts that switch quickly between active and recovered states are identified as “flapping” and notifications are reduced. nullable: true type: object properties: look_back_window: - description: >- - The minimum number of runs in which the threshold must - be met. + description: The minimum number of runs in which the threshold must be met. maximum: 20 minimum: 2 type: number status_change_threshold: - description: >- - The minimum number of times an alert must switch - states in the look back window. + description: The minimum number of times an alert must switch states in the look back window. maximum: 20 minimum: 2 type: number @@ -2887,24 +2866,17 @@ paths: - active flapping: additionalProperties: false - description: >- - When flapping detection is turned on, alerts that switch - quickly between active and recovered states are identified - as “flapping” and notifications are reduced. + description: When flapping detection is turned on, alerts that switch quickly between active and recovered states are identified as “flapping” and notifications are reduced. nullable: true type: object properties: look_back_window: - description: >- - The minimum number of runs in which the threshold must - be met. + description: The minimum number of runs in which the threshold must be met. maximum: 20 minimum: 2 type: number status_change_threshold: - description: >- - The minimum number of times an alert must switch states - in the look back window. + description: The minimum number of times an alert must switch states in the look back window. maximum: 20 minimum: 2 type: number @@ -3194,24 +3166,17 @@ paths: - last_execution_date flapping: additionalProperties: false - description: >- - When flapping detection is turned on, alerts that switch - quickly between active and recovered states are identified - as “flapping” and notifications are reduced. + description: When flapping detection is turned on, alerts that switch quickly between active and recovered states are identified as “flapping” and notifications are reduced. nullable: true type: object properties: look_back_window: - description: >- - The minimum number of runs in which the threshold must - be met. + description: The minimum number of runs in which the threshold must be met. maximum: 20 minimum: 2 type: number status_change_threshold: - description: >- - The minimum number of times an alert must switch - states in the look back window. + description: The minimum number of times an alert must switch states in the look back window. maximum: 20 minimum: 2 type: number @@ -4241,24 +4206,17 @@ paths: - last_execution_date flapping: additionalProperties: false - description: >- - When flapping detection is turned on, alerts that switch - quickly between active and recovered states are identified - as “flapping” and notifications are reduced. + description: When flapping detection is turned on, alerts that switch quickly between active and recovered states are identified as “flapping” and notifications are reduced. nullable: true type: object properties: look_back_window: - description: >- - The minimum number of runs in which the threshold must - be met. + description: The minimum number of runs in which the threshold must be met. maximum: 20 minimum: 2 type: number status_change_threshold: - description: >- - The minimum number of times an alert must switch - states in the look back window. + description: The minimum number of times an alert must switch states in the look back window. maximum: 20 minimum: 2 type: number @@ -6708,14 +6666,9 @@ paths: - cases /api/cases/{caseId}/files: post: - description: > - Attach a file to a case. You must have `all` privileges for the - **Cases** feature in the **Management**, **Observability**, or - **Security** section of the Kibana feature privileges, depending on the - owner of the case you're updating. The request must include: - + description: | + Attach a file to a case. You must have `all` privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the case you're updating. The request must include: - The `Content-Type: multipart/form-data` HTTP header. - - The location of the file that is being uploaded. operationId: addCaseFileDefaultSpace parameters: @@ -22174,7 +22127,7 @@ paths: summary: Update package settings tags: - Elastic Package Manager (EPM) - /api/fleet/epm/packages/{pkgName}/{pkgVersion}/{filePath*}: + /api/fleet/epm/packages/{pkgName}/{pkgVersion}/{filePath}: get: operationId: get-fleet-epm-packages-pkgname-pkgversion-filepath parameters: @@ -43715,9 +43668,7 @@ components: - $ref: '#/components/schemas/Cases_add_user_comment_request_properties' title: Add case comment request Cases_add_case_file_request: - description: >- - Defines the file that will be attached to the case. Optional parameters - will be generated automatically from the file metadata if not defined. + description: Defines the file that will be attached to the case. Optional parameters will be generated automatically from the file metadata if not defined. type: object properties: file: @@ -43725,10 +43676,7 @@ components: format: binary type: string filename: - description: >- - The desired name of the file being attached to the case, it can be - different than the name of the file in the filesystem. **This should - not include the file extension.** + description: The desired name of the file being attached to the case, it can be different than the name of the file in the filesystem. **This should not include the file extension.** type: string required: - file diff --git a/oas_docs/package-lock.json b/oas_docs/package-lock.json index 6527a6ee6a5dd..70fff86254f65 100644 --- a/oas_docs/package-lock.json +++ b/oas_docs/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@redocly/cli": "^1.25.7", + "@redocly/cli": "^1.25.11", "bump-cli": "^2.8.4" } }, @@ -518,6 +518,7 @@ "version": "1.25.11", "resolved": "https://registry.npmjs.org/@redocly/cli/-/cli-1.25.11.tgz", "integrity": "sha512-dttBsmLnnbTlJCTa+s7Sy+qtXDq692n7Ru3nUUIHp9XdCbhXIHWhpc8uAl+GmR4MGbVe8ohATl3J+zX3aFy82A==", + "license": "MIT", "dependencies": { "@redocly/openapi-core": "1.25.11", "abort-controller": "^3.0.0", diff --git a/oas_docs/package.json b/oas_docs/package.json index d007a9881acad..3f6cae5c044b3 100644 --- a/oas_docs/package.json +++ b/oas_docs/package.json @@ -8,7 +8,7 @@ }, "dependencies": { "bump-cli": "^2.8.4", - "@redocly/cli": "^1.25.7" + "@redocly/cli": "^1.25.11" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" diff --git a/package.json b/package.json index fb6b8b093560c..acb1a118284ed 100644 --- a/package.json +++ b/package.json @@ -1022,7 +1022,7 @@ "@langchain/langgraph": "0.2.19", "@langchain/openai": "^0.3.11", "@langtrase/trace-attributes": "^3.0.8", - "@launchdarkly/node-server-sdk": "^9.7.0", + "@launchdarkly/node-server-sdk": "^9.7.1", "@launchdarkly/openfeature-node-server": "^1.0.0", "@loaders.gl/core": "^3.4.7", "@loaders.gl/json": "^3.4.7", @@ -1633,7 +1633,7 @@ "@types/resolve": "^1.20.1", "@types/scheduler": "^0.23.0", "@types/seedrandom": ">=2.0.0 <4.0.0", - "@types/selenium-webdriver": "^4.1.26", + "@types/selenium-webdriver": "^4.1.27", "@types/semver": "^7.5.8", "@types/set-value": "^2.0.0", "@types/sinon": "^7.0.13", @@ -1680,7 +1680,7 @@ "buildkite-test-collector": "^1.7.0", "callsites": "^3.1.0", "chance": "1.0.18", - "chromedriver": "^130.0.1", + "chromedriver": "^130.0.4", "clarify": "^2.2.0", "clean-webpack-plugin": "^3.0.0", "cli-progress": "^3.12.0", @@ -1731,7 +1731,7 @@ "file-loader": "^4.2.0", "find-cypress-specs": "^1.41.4", "form-data": "^4.0.0", - "geckodriver": "^4.5.1", + "geckodriver": "^5.0.0", "gulp-brotli": "^3.0.0", "gulp-postcss": "^9.0.1", "gulp-terser": "^2.1.0", @@ -1806,7 +1806,7 @@ "rxjs-marbles": "^7.0.1", "sass-embedded": "^1.78.0", "sass-loader": "^10.5.1", - "selenium-webdriver": "^4.25.0", + "selenium-webdriver": "^4.26.0", "sharp": "0.32.6", "simple-git": "^3.16.0", "sinon": "^7.4.2", diff --git a/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap b/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap index 1c1f5bed02a0e..ccf5fad20a71a 100644 --- a/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap +++ b/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap @@ -228,7 +228,7 @@ OK response oas-test-version-2", "x-discontinued": "route discontinued version or date", }, }, - "/foo/{id}/{path*}": Object { + "/foo/{id}/{path}": Object { "delete": Object { "description": "route description", "operationId": "delete-foo-id-path", @@ -570,7 +570,7 @@ OK response oas-test-version-2", ], }, }, - "/no-xsrf/{id}/{path*}": Object { + "/no-xsrf/{id}/{path}": Object { "post": Object { "deprecated": true, "operationId": "post-no-xsrf-id-path-2", diff --git a/packages/kbn-router-to-openapispec/src/generate_oas.test.fixture.ts b/packages/kbn-router-to-openapispec/src/generate_oas.test.fixture.ts index f4ba66f992134..dedba5036d7ef 100644 --- a/packages/kbn-router-to-openapispec/src/generate_oas.test.fixture.ts +++ b/packages/kbn-router-to-openapispec/src/generate_oas.test.fixture.ts @@ -151,7 +151,7 @@ export const sharedOas = { tags: ['versioned'], }, }, - '/foo/{id}/{path*}': { + '/foo/{id}/{path}': { get: { description: 'route description', operationId: 'get-foo-id-path', diff --git a/packages/kbn-router-to-openapispec/src/generate_oas.test.ts b/packages/kbn-router-to-openapispec/src/generate_oas.test.ts index 25b786ac7c2c7..76a4f560006b3 100644 --- a/packages/kbn-router-to-openapispec/src/generate_oas.test.ts +++ b/packages/kbn-router-to-openapispec/src/generate_oas.test.ts @@ -314,9 +314,9 @@ describe('generateOpenApiDocument', () => { } ); // router paths - expect(result.paths['/1-1/{id}/{path*}']!.get!.tags).toEqual(['1', '2']); - expect(result.paths['/1-2/{id}/{path*}']!.get!.tags).toEqual(['1']); - expect(result.paths['/2-1/{id}/{path*}']!.get!.tags).toEqual([]); + expect(result.paths['/1-1/{id}/{path}']!.get!.tags).toEqual(['1', '2']); + expect(result.paths['/1-2/{id}/{path}']!.get!.tags).toEqual(['1']); + expect(result.paths['/2-1/{id}/{path}']!.get!.tags).toEqual([]); // versioned router paths expect(result.paths['/v1-1']!.get!.tags).toEqual(['v1']); expect(result.paths['/v1-2']!.get!.tags).toEqual(['v2', 'v3']); @@ -392,17 +392,17 @@ describe('generateOpenApiDocument', () => { ); // router paths - expect(result.paths['/1-1/{id}/{path*}']!.get).toMatchObject({ + expect(result.paths['/1-1/{id}/{path}']!.get).toMatchObject({ 'x-state': 'Technical Preview', }); - expect(result.paths['/1-2/{id}/{path*}']!.get).toMatchObject({ + expect(result.paths['/1-2/{id}/{path}']!.get).toMatchObject({ 'x-state': 'Beta', }); - expect(result.paths['/1-3/{id}/{path*}']!.get).not.toMatchObject({ + expect(result.paths['/1-3/{id}/{path}']!.get).not.toMatchObject({ 'x-state': expect.any(String), }); - expect(result.paths['/2-1/{id}/{path*}']!.get).not.toMatchObject({ + expect(result.paths['/2-1/{id}/{path}']!.get).not.toMatchObject({ 'x-state': expect.any(String), }); diff --git a/packages/kbn-router-to-openapispec/src/util.test.ts b/packages/kbn-router-to-openapispec/src/util.test.ts index f9692e57e1f50..e3011aa1a5a73 100644 --- a/packages/kbn-router-to-openapispec/src/util.test.ts +++ b/packages/kbn-router-to-openapispec/src/util.test.ts @@ -17,8 +17,9 @@ import { getPathParameters, createOpIdGenerator, GetOpId, + assignToPaths, + extractTags, } from './util'; -import { assignToPaths, extractTags } from './util'; describe('extractTags', () => { test.each([ @@ -115,9 +116,11 @@ describe('assignToPaths', () => { const paths = {}; assignToPaths(paths, '/foo', {}); assignToPaths(paths, '/bar/{id?}', {}); + assignToPaths(paths, '/bar/file/{path*}', {}); expect(paths).toEqual({ '/foo': {}, '/bar/{id}': {}, + '/bar/file/{path}': {}, }); }); }); @@ -320,7 +323,7 @@ describe('createOpIdGenerator', () => { { input: { method: 'get', - path: '/api/my/resource/{path*}', + path: '/api/my/resource/{path}', }, output: 'get-my-resource-path', }, diff --git a/packages/kbn-router-to-openapispec/src/util.ts b/packages/kbn-router-to-openapispec/src/util.ts index a5718fa92120f..1088259e73d05 100644 --- a/packages/kbn-router-to-openapispec/src/util.ts +++ b/packages/kbn-router-to-openapispec/src/util.ts @@ -132,7 +132,7 @@ export const assignToPaths = ( path: string, pathObject: OpenAPIV3.PathItemObject ): void => { - const pathName = path.replace('?', ''); + const pathName = path.replace(/[\?\*]/, ''); paths[pathName] = { ...paths[pathName], ...pathObject }; }; diff --git a/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.test.ts b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.test.ts index 0248cdf9b6e36..04cb76f4441c5 100644 --- a/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.test.ts +++ b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.test.ts @@ -10,6 +10,7 @@ import { defaultErrorMessage, buildMutedRulesFilter, buildEntityFlyoutPreviewQuery, + buildEntityAlertsQuery, } from './helpers'; const fallbackMessage = 'thisIsAFallBackMessage'; @@ -182,4 +183,78 @@ describe('test helper methods', () => { expect(buildEntityFlyoutPreviewQuery(field)).toEqual(expectedQuery); }); }); + + describe('buildEntityAlertsQuery', () => { + const getExpectedAlertsQuery = (size?: number) => { + return { + size: size || 0, + _source: false, + fields: [ + '_id', + '_index', + 'kibana.alert.rule.uuid', + 'kibana.alert.severity', + 'kibana.alert.rule.name', + 'kibana.alert.workflow_status', + ], + query: { + bool: { + filter: [ + { + bool: { + must: [], + filter: [ + { + match_phrase: { + 'host.name': { + query: 'exampleHost', + }, + }, + }, + ], + should: [], + must_not: [], + }, + }, + { + range: { + '@timestamp': { + gte: 'Today', + lte: 'Tomorrow', + }, + }, + }, + { + terms: { + 'kibana.alert.workflow_status': ['open', 'acknowledged'], + }, + }, + ], + }, + }, + }; + }; + + it('should return the correct query when given all params', () => { + const field = 'host.name'; + const query = 'exampleHost'; + const to = 'Tomorrow'; + const from = 'Today'; + const size = 100; + + expect(buildEntityAlertsQuery(field, to, from, query, size)).toEqual( + getExpectedAlertsQuery(size) + ); + }); + + it('should return the correct query when not given size', () => { + const field = 'host.name'; + const query = 'exampleHost'; + const to = 'Tomorrow'; + const from = 'Today'; + const size = undefined; + + expect(buildEntityAlertsQuery(field, to, from, query)).toEqual(getExpectedAlertsQuery(size)); + }); + }); }); diff --git a/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts index 7039c99af6d53..bd531fa63804f 100644 --- a/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts +++ b/x-pack/packages/kbn-cloud-security-posture/common/utils/helpers.ts @@ -5,6 +5,7 @@ * 2.0. */ import { QueryDslQueryContainer } from '@kbn/data-views-plugin/common/types'; + import { i18n } from '@kbn/i18n'; import type { CspBenchmarkRulesStates } from '../schema/rules/latest'; @@ -62,3 +63,59 @@ export const buildEntityFlyoutPreviewQuery = (field: string, queryValue?: string }, }; }; + +export const buildEntityAlertsQuery = ( + field: string, + to: string, + from: string, + queryValue?: string, + size?: number +) => { + return { + size: size || 0, + _source: false, + fields: [ + '_id', + '_index', + 'kibana.alert.rule.uuid', + 'kibana.alert.severity', + 'kibana.alert.rule.name', + 'kibana.alert.workflow_status', + ], + query: { + bool: { + filter: [ + { + bool: { + must: [], + filter: [ + { + match_phrase: { + [field]: { + query: queryValue, + }, + }, + }, + ], + should: [], + must_not: [], + }, + }, + { + range: { + '@timestamp': { + gte: from, + lte: to, + }, + }, + }, + { + terms: { + 'kibana.alert.workflow_status': ['open', 'acknowledged'], + }, + }, + ], + }, + }, + }; +}; diff --git a/x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_misconfiguration_findings.ts b/x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_misconfiguration_findings.ts index 40880b132537d..9bbaedf587dde 100644 --- a/x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_misconfiguration_findings.ts +++ b/x-pack/packages/kbn-cloud-security-posture/public/src/hooks/use_misconfiguration_findings.ts @@ -40,10 +40,11 @@ export const useMisconfigurationFindings = (options: UseCspOptions) => { params: buildMisconfigurationsFindingsQuery(options, rulesStates!), }) ); - if (!aggregations) throw new Error('expected aggregations to be defined'); + if (!aggregations && options.ignore_unavailable === false) + throw new Error('expected aggregations to be defined'); return { - count: getMisconfigurationAggregationCount(aggregations.count.buckets), + count: getMisconfigurationAggregationCount(aggregations?.count.buckets), rows: hits.hits.map((finding) => ({ result: finding._source?.result, rule: finding?._source?.rule, diff --git a/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_chart_component_wrapper.tsx b/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_chart_component_wrapper.tsx index 15159d7adb60c..c96f8376a2ad9 100644 --- a/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_chart_component_wrapper.tsx +++ b/x-pack/plugins/aiops/public/embeddables/change_point_chart/embeddable_chart_component_wrapper.tsx @@ -72,8 +72,8 @@ export const ChartGridEmbeddableWrapper: FC = ({ mergedQuery.bool.filter.push({ range: { [dataView.timeFieldName!]: { - from: searchBounds.min?.valueOf(), - to: searchBounds.max?.valueOf(), + gte: searchBounds.min?.valueOf(), + lte: searchBounds.max?.valueOf(), format: 'epoch_millis', }, }, diff --git a/x-pack/plugins/embeddable_enhanced/public/plugin.ts b/x-pack/plugins/embeddable_enhanced/public/plugin.ts index f05982af78947..a76f33f095951 100644 --- a/x-pack/plugins/embeddable_enhanced/public/plugin.ts +++ b/x-pack/plugins/embeddable_enhanced/public/plugin.ts @@ -148,8 +148,8 @@ export class EmbeddableEnhancedPlugin ); const api: DynamicActionStorageApi = { dynamicActionsState$, - setDynamicActions: (newState) => { - dynamicActionsState$.next(newState); + setDynamicActions: (enhancements) => { + dynamicActionsState$.next(getDynamicActionsState(enhancements)); }, }; const storage = new DynamicActionStorage(uuid, getTitle, api); diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_create.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_create.test.tsx index a27fb82fb82b0..7ba6832b8833d 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_create.test.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_create.test.tsx @@ -52,6 +52,15 @@ jest.mock('@elastic/eui', () => { }} /> ), + EuiSuperSelect: (props: any) => ( + { + props.onChange(e.target.value); + }} + /> + ), }; }); @@ -301,6 +310,15 @@ describe('', () => { expect(find('stepTitle').text()).toEqual('Index settings (optional)'); }); + it('should display a warning callout displaying the selected index mode', async () => { + const { exists, find } = testBed; + + expect(exists('indexModeCallout')).toBe(true); + expect(find('indexModeCallout').text()).toContain( + 'The index.mode setting has been set to Standard within template Logistics.' + ); + }); + it('should not allow invalid json', async () => { const { form, actions } = testBed; @@ -426,6 +444,53 @@ describe('', () => { }); }); + describe('logistics (step 1)', () => { + beforeEach(async () => { + await act(async () => { + testBed = await setup(httpSetup); + }); + testBed.component.update(); + }); + + it('setting index pattern to logs-*-* should set the index mode to logsdb', async () => { + const { component, actions } = testBed; + // Logistics + await actions.completeStepOne({ name: 'my_logs_template', indexPatterns: ['logs-*-*'] }); + // Component templates + await actions.completeStepTwo(); + // Index settings + await actions.completeStepThree('{}'); + // Mappings + await actions.completeStepFour(); + // Aliases + await actions.completeStepFive(); + + await act(async () => { + actions.clickNextButton(); + }); + component.update(); + + expect(httpSetup.post).toHaveBeenLastCalledWith( + `${API_BASE_PATH}/index_templates`, + expect.objectContaining({ + body: JSON.stringify({ + name: 'my_logs_template', + indexPatterns: ['logs-*-*'], + allowAutoCreate: 'NO_OVERWRITE', + indexMode: 'logsdb', + dataStream: {}, + _kbnMeta: { + type: 'default', + hasDatastream: false, + isLegacy: false, + }, + template: {}, + }), + }) + ); + }); + }); + describe('review (step 6)', () => { beforeEach(async () => { await act(async () => { @@ -529,6 +594,7 @@ describe('', () => { name: TEMPLATE_NAME, indexPatterns: DEFAULT_INDEX_PATTERNS, allowAutoCreate: 'TRUE', + indexMode: 'time_series', }); // Component templates await actions.completeStepTwo('test_component_template_1'); @@ -557,6 +623,7 @@ describe('', () => { name: TEMPLATE_NAME, indexPatterns: DEFAULT_INDEX_PATTERNS, allowAutoCreate: 'TRUE', + indexMode: 'time_series', dataStream: {}, _kbnMeta: { type: 'default', diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_edit.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_edit.test.tsx index dac7dadfa2557..bbd1d24c7906d 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_edit.test.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_edit.test.tsx @@ -169,6 +169,7 @@ describe('', () => { indexPatterns: ['myPattern*'], version: 1, allowAutoCreate: 'NO_OVERWRITE', + indexMode: 'standard', dataStream: { hidden: true, anyUnknownKey: 'should_be_kept', diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_form.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_form.helpers.ts index 7977c4373d765..7c02608d4e3f7 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_form.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_form.helpers.ts @@ -148,6 +148,7 @@ export const formSetup = async (initTestBed: SetupFunc) => { enableDataStream, lifecycle, allowAutoCreate, + indexMode, }: Partial & { enableDataStream?: boolean } = {}) => { const { component, form, find } = testBed; @@ -204,6 +205,10 @@ export const formSetup = async (initTestBed: SetupFunc) => { radioOption.simulate('change', { target: { checked: true } }); component.update(); } + + if (indexMode) { + form.setSelectValue('indexModeField', indexMode); + } }); component.update(); @@ -356,6 +361,7 @@ export type TestSubjects = | 'mappingsEditorFieldEdit' | 'mockCodeEditor' | 'mockComboBox' + | 'mockSuperSelect' | 'nameField' | 'nameField.input' | 'nameParameterInput' @@ -364,6 +370,8 @@ export type TestSubjects = | 'orderField.input' | 'priorityField.input' | 'dataStreamField.input' + | 'indexModeField' + | 'indexModeCallout' | 'dataRetentionToggle.input' | 'allowAutoCreateField.input' | 'pageTitle' diff --git a/x-pack/plugins/index_management/common/constants/index.ts b/x-pack/plugins/index_management/common/constants/index.ts index 49e2a7f9505a9..81e50fa8b75be 100644 --- a/x-pack/plugins/index_management/common/constants/index.ts +++ b/x-pack/plugins/index_management/common/constants/index.ts @@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n'; export { BASE_PATH } from './base_path'; export { API_BASE_PATH, INTERNAL_API_BASE_PATH } from './api_base_path'; export { INVALID_INDEX_PATTERN_CHARS, INVALID_TEMPLATE_NAME_CHARS } from './invalid_characters'; +export * from './index_modes'; export * from './index_statuses'; // Since each index can have a max length or 255 characters and the max length of diff --git a/x-pack/plugins/index_management/common/constants/index_modes.ts b/x-pack/plugins/index_management/common/constants/index_modes.ts new file mode 100644 index 0000000000000..65099cf4e4f8b --- /dev/null +++ b/x-pack/plugins/index_management/common/constants/index_modes.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 STANDARD_INDEX_MODE = 'standard'; +export const LOGSDB_INDEX_MODE = 'logsdb'; +export const TIME_SERIES_MODE = 'time_series'; diff --git a/x-pack/plugins/index_management/common/lib/template_serialization.test.ts b/x-pack/plugins/index_management/common/lib/template_serialization.test.ts index 8f9f73c334a9f..cb86de2660fd3 100644 --- a/x-pack/plugins/index_management/common/lib/template_serialization.test.ts +++ b/x-pack/plugins/index_management/common/lib/template_serialization.test.ts @@ -6,7 +6,8 @@ */ import { deserializeTemplate, serializeTemplate } from './template_serialization'; -import { TemplateDeserialized, TemplateSerialized } from '../types'; +import { TemplateDeserialized, TemplateSerialized, IndexMode } from '../types'; +import { STANDARD_INDEX_MODE, LOGSDB_INDEX_MODE, TIME_SERIES_MODE } from '../constants'; const defaultSerializedTemplate: TemplateSerialized = { template: {}, @@ -17,6 +18,7 @@ const defaultSerializedTemplate: TemplateSerialized = { const defaultDeserializedTemplate: TemplateDeserialized = { name: 'my_template', indexPatterns: ['test'], + indexMode: STANDARD_INDEX_MODE, _kbnMeta: { type: 'default', hasDatastream: true, @@ -26,12 +28,13 @@ const defaultDeserializedTemplate: TemplateDeserialized = { const allowAutoCreateRadioOptions = ['NO_OVERWRITE', 'TRUE', 'FALSE']; const allowAutoCreateSerializedValues = [undefined, true, false]; +const indexModeValues = [STANDARD_INDEX_MODE, LOGSDB_INDEX_MODE, TIME_SERIES_MODE, undefined]; describe('Template serialization', () => { describe('serialization of allow_auto_create parameter', () => { describe('deserializeTemplate()', () => { allowAutoCreateSerializedValues.forEach((value, index) => { - test(`correctly deserializes ${value} value`, () => { + test(`correctly deserializes ${value} allow_auto_create value`, () => { expect( deserializeTemplate({ ...defaultSerializedTemplate, @@ -41,11 +44,29 @@ describe('Template serialization', () => { ).toHaveProperty('allowAutoCreate', allowAutoCreateRadioOptions[index]); }); }); + + indexModeValues.forEach((value) => { + test(`correctly deserializes ${value} index mode settings value`, () => { + expect( + deserializeTemplate({ + ...defaultSerializedTemplate, + name: 'my_template', + template: { + settings: { + index: { + mode: value, + }, + }, + }, + }) + ).toHaveProperty('indexMode', value ?? STANDARD_INDEX_MODE); + }); + }); }); describe('serializeTemplate()', () => { allowAutoCreateRadioOptions.forEach((option, index) => { - test(`correctly serializes ${option} radio option`, () => { + test(`correctly serializes ${option} allowAutoCreate radio option`, () => { expect( serializeTemplate({ ...defaultDeserializedTemplate, @@ -54,6 +75,18 @@ describe('Template serialization', () => { ).toHaveProperty('allow_auto_create', allowAutoCreateSerializedValues[index]); }); }); + + // Only use the first three values (omit undefined) + indexModeValues.slice(0, 3).forEach((value) => { + test(`correctly serializes ${value} indexMode option`, () => { + expect( + serializeTemplate({ + ...defaultDeserializedTemplate, + indexMode: value as IndexMode, + }) + ).toHaveProperty('template.settings.index.mode', value); + }); + }); }); }); }); diff --git a/x-pack/plugins/index_management/common/lib/template_serialization.ts b/x-pack/plugins/index_management/common/lib/template_serialization.ts index 0ed52e3f04ba0..999023704559c 100644 --- a/x-pack/plugins/index_management/common/lib/template_serialization.ts +++ b/x-pack/plugins/index_management/common/lib/template_serialization.ts @@ -11,9 +11,15 @@ import { TemplateSerialized, TemplateListItem, TemplateType, + IndexMode, } from '../types'; import { deserializeESLifecycle } from './data_stream_utils'; -import { allowAutoCreateRadioValues, allowAutoCreateRadioIds } from '../constants'; +import { + allowAutoCreateRadioValues, + allowAutoCreateRadioIds, + STANDARD_INDEX_MODE, + LOGSDB_INDEX_MODE, +} from '../constants'; const hasEntries = (data: object = {}) => Object.entries(data).length > 0; @@ -26,6 +32,7 @@ export function serializeTemplate(templateDeserialized: TemplateDeserialized): T composedOf, ignoreMissingComponentTemplates, dataStream, + indexMode, _meta, allowAutoCreate, deprecated, @@ -34,7 +41,16 @@ export function serializeTemplate(templateDeserialized: TemplateDeserialized): T return { version, priority, - template, + template: { + ...template, + settings: { + ...template?.settings, + index: { + ...template?.settings?.index, + mode: indexMode, + }, + }, + }, index_patterns: indexPatterns, data_stream: dataStream, composed_of: composedOf, @@ -75,6 +91,11 @@ export function deserializeTemplate( const ilmPolicyName = settings?.index?.lifecycle?.name; + const indexMode = (settings?.index?.mode ?? + (indexPatterns.some((pattern) => pattern === 'logs-*-*') + ? LOGSDB_INDEX_MODE + : STANDARD_INDEX_MODE)) as IndexMode; + const deserializedTemplate: TemplateDeserialized = { name, version, @@ -82,6 +103,7 @@ export function deserializeTemplate( ...(template.lifecycle ? { lifecycle: deserializeESLifecycle(template.lifecycle) } : {}), indexPatterns: indexPatterns.sort(), template, + indexMode, ilmPolicy: ilmPolicyName ? { name: ilmPolicyName } : undefined, composedOf: composedOf ?? [], ignoreMissingComponentTemplates: ignoreMissingComponentTemplates ?? [], diff --git a/x-pack/plugins/index_management/common/types/index.ts b/x-pack/plugins/index_management/common/types/index.ts index ef2e8a389c079..7ec100bc1d366 100644 --- a/x-pack/plugins/index_management/common/types/index.ts +++ b/x-pack/plugins/index_management/common/types/index.ts @@ -19,6 +19,7 @@ export type { DataStream, DataStreamIndex, DataRetention, + IndexMode, } from './data_streams'; export * from './component_templates'; diff --git a/x-pack/plugins/index_management/common/types/templates.ts b/x-pack/plugins/index_management/common/types/templates.ts index b05a29a961a73..ab4614200c0b5 100644 --- a/x-pack/plugins/index_management/common/types/templates.ts +++ b/x-pack/plugins/index_management/common/types/templates.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { DataRetention, DataStream } from './data_streams'; +import { DataRetention, DataStream, IndexMode } from './data_streams'; import { IndexSettings } from './indices'; import { Aliases } from './aliases'; import { Mappings } from './mappings'; @@ -51,6 +51,7 @@ export interface TemplateDeserialized { priority?: number; // Composable template only allowAutoCreate: string; order?: number; // Legacy template only + indexMode: IndexMode; ilmPolicy?: { name: string; }; diff --git a/x-pack/plugins/index_management/public/application/components/shared/components/wizard_steps/step_settings.tsx b/x-pack/plugins/index_management/public/application/components/shared/components/wizard_steps/step_settings.tsx index 9b26ed38223c4..38a11f03c7ee6 100644 --- a/x-pack/plugins/index_management/public/application/components/shared/components/wizard_steps/step_settings.tsx +++ b/x-pack/plugins/index_management/public/application/components/shared/components/wizard_steps/step_settings.tsx @@ -16,6 +16,8 @@ import { EuiFormRow, EuiText, EuiCode, + EuiCallOut, + EuiLink, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { CodeEditor } from '@kbn/code-editor'; @@ -23,15 +25,19 @@ import { CodeEditor } from '@kbn/code-editor'; import { Forms } from '../../../../../shared_imports'; import { useJsonStep } from './use_json_step'; import { documentationService } from '../../../mappings_editor/shared_imports'; +import { indexModeLabels } from '../../../../lib/index_mode_labels'; +import { IndexMode } from '../../../../../../common/types'; interface Props { onChange: (content: Forms.Content) => void; esDocsBase: string; defaultValue?: { [key: string]: any }; + indexMode?: IndexMode; } export const StepSettings: React.FunctionComponent = React.memo( - ({ defaultValue = {}, onChange, esDocsBase }) => { + ({ defaultValue = {}, onChange, esDocsBase, indexMode }) => { + const { navigateToStep } = Forms.useFormWizardContext(); const { jsonContent, setJsonContent, error } = useJsonStep({ defaultValue, onChange, @@ -80,6 +86,47 @@ export const StepSettings: React.FunctionComponent = React.memo( + {indexMode && ( + <> + + {i18n.translate( + 'xpack.idxMgmt.formWizard.stepSettings.indexModeCallout.indexModeSettingLabel', + { + defaultMessage: 'index.mode', + } + )} + + ), + indexMode: indexModeLabels[indexMode], + logisticsLink: ( + navigateToStep(0)}> + {i18n.translate( + 'xpack.idxMgmt.formWizard.stepSettings.indexModeCallout.logisticsLinkLabel', + { + defaultMessage: 'Logistics', + } + )} + + ), + }} + /> + } + color="warning" + iconType="warning" + data-test-subj="indexModeCallout" + /> + + + + )} + {/* Settings code editor */} TemplateDeserialized; } -export const StepSettingsContainer = React.memo(({ esDocsBase }: Props) => { +export const StepSettingsContainer = React.memo(({ esDocsBase, getTemplateData }: Props) => { const { defaultValue, updateContent } = Forms.useContent( 'settings' ); + const { getData } = Forms.useMultiContentContext(); + + let indexMode; + if (getTemplateData) { + const wizardContent = getData(); + // Build the current template object, providing the wizard content data + const template = getTemplateData(wizardContent); + indexMode = template.indexMode; + } return ( - + ); }); diff --git a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx index d73d95600e5b1..9742f2eec525f 100644 --- a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx +++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_logistics.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useEffect, useCallback } from 'react'; +import React, { useEffect, useCallback, Fragment } from 'react'; import { EuiFlexGroup, EuiFlexItem, @@ -14,6 +14,7 @@ import { EuiSpacer, EuiLink, EuiCode, + EuiText, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; @@ -34,7 +35,13 @@ import { UnitField, timeUnits } from '../../shared'; import { DataRetention } from '../../../../../common'; import { documentationService } from '../../../services/documentation'; import { schemas, nameConfig, nameConfigWithoutValidations } from '../template_form_schemas'; -import { allowAutoCreateRadios } from '../../../../../common/constants'; +import { + allowAutoCreateRadios, + STANDARD_INDEX_MODE, + TIME_SERIES_MODE, + LOGSDB_INDEX_MODE, +} from '../../../../../common/constants'; +import { indexModeLabels, indexModeDescriptions } from '../../../lib/index_mode_labels'; // Create or Form components with partial props that are common to all instances const UseField = getUseField({ component: Field }); @@ -91,6 +98,54 @@ function getFieldsMeta(esDocsBase: string) { ), testSubject: 'dataStreamField', }, + indexMode: { + title: i18n.translate('xpack.idxMgmt.templateForm.stepLogistics.indexModeTitle', { + defaultMessage: 'Data stream index mode', + }), + description: i18n.translate('xpack.idxMgmt.templateForm.stepLogistics.indexModeDescription', { + defaultMessage: + 'The index.mode setting is used to control settings applied in specific domains like ingestions of time series data or logs.', + }), + options: [ + { + value: STANDARD_INDEX_MODE, + inputDisplay: indexModeLabels[STANDARD_INDEX_MODE], + dropdownDisplay: ( + + {indexModeLabels[STANDARD_INDEX_MODE]} + +

{indexModeDescriptions[STANDARD_INDEX_MODE]}

+
+
+ ), + }, + { + value: TIME_SERIES_MODE, + inputDisplay: indexModeLabels[TIME_SERIES_MODE], + dropdownDisplay: ( + + {indexModeLabels[TIME_SERIES_MODE]} + +

{indexModeDescriptions[TIME_SERIES_MODE]}

+
+
+ ), + }, + { + value: LOGSDB_INDEX_MODE, + inputDisplay: indexModeLabels[LOGSDB_INDEX_MODE], + dropdownDisplay: ( + + {indexModeLabels[LOGSDB_INDEX_MODE]} + +

{indexModeDescriptions[LOGSDB_INDEX_MODE]}

+
+
+ ), + }, + ], + testSubject: 'indexModeField', + }, order: { title: i18n.translate('xpack.idxMgmt.templateForm.stepLogistics.orderTitle', { defaultMessage: 'Merge order', @@ -198,21 +253,37 @@ export const StepLogistics: React.FunctionComponent = React.memo( isValid: isFormValid, getErrors: getFormErrors, getFormData, + setFieldValue, } = form; - const [{ addMeta, doCreateDataStream, lifecycle }] = useFormData<{ - addMeta: boolean; - lifecycle: DataRetention; - doCreateDataStream: boolean; - }>({ - form, - watch: [ - 'addMeta', - 'lifecycle.enabled', - 'lifecycle.infiniteDataRetention', - 'doCreateDataStream', - ], - }); + const [{ addMeta, doCreateDataStream, lifecycle, indexPatterns: indexPatternsField }] = + useFormData<{ + addMeta: boolean; + lifecycle: DataRetention; + doCreateDataStream: boolean; + indexPatterns: string[]; + }>({ + form, + watch: [ + 'addMeta', + 'lifecycle.enabled', + 'lifecycle.infiniteDataRetention', + 'doCreateDataStream', + 'indexPatterns', + ], + }); + + useEffect(() => { + if ( + indexPatternsField && + indexPatternsField.length === 1 && + indexPatternsField[0] === 'logs-*-*' && + // Only set index mode if index pattern was changed + defaultValue.indexPatterns !== indexPatternsField + ) { + setFieldValue('indexMode', LOGSDB_INDEX_MODE); + } + }, [defaultValue.indexPatterns, indexPatternsField, setFieldValue]); /** * When the consumer call validate() on this step, we submit the form so it enters the "isSubmitted" state @@ -234,6 +305,7 @@ export const StepLogistics: React.FunctionComponent = React.memo( name, indexPatterns, createDataStream, + indexMode, order, priority, version, @@ -312,6 +384,21 @@ export const StepLogistics: React.FunctionComponent = React.memo( )} + {doCreateDataStream && ( + + + + )} + {/* Since data stream and data retention are settings that are only allowed for non legacy, we only need to check if data stream is set to true to show the data retention. diff --git a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx index 9cb5c481b6b50..593655da62fef 100644 --- a/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx +++ b/x-pack/plugins/index_management/public/application/components/template_form/steps/step_review.tsx @@ -22,7 +22,7 @@ import { EuiCodeBlock, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; -import { getIndexModeLabel } from '../../../lib/index_mode_labels'; +import { indexModeLabels } from '../../../lib/index_mode_labels'; import { allowAutoCreateRadioIds } from '../../../../../common/constants'; import { serializers } from '../../../../shared_imports'; @@ -89,6 +89,7 @@ export const StepReview: React.FunctionComponent = React.memo( const { name, indexPatterns, + indexMode, version, order, template: indexTemplate, @@ -277,9 +278,7 @@ export const StepReview: React.FunctionComponent = React.memo( /> - {getIndexModeLabel( - serializedSettings?.['index.mode'] ?? serializedSettings?.index?.mode - )} + {indexModeLabels[indexMode]} {/* Mappings */} diff --git a/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx b/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx index 2da3eef609a65..53b53a6ebdeee 100644 --- a/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx +++ b/x-pack/plugins/index_management/public/application/components/template_form/template_form.tsx @@ -11,7 +11,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { EuiSpacer, EuiButton, EuiPageHeader } from '@elastic/eui'; import { ScopedHistory } from '@kbn/core/public'; -import { allowAutoCreateRadioIds } from '../../../../common/constants'; +import { allowAutoCreateRadioIds, STANDARD_INDEX_MODE } from '../../../../common/constants'; import { TemplateDeserialized } from '../../../../common'; import { serializers, Forms, GlobalFlyout } from '../../../shared_imports'; import { @@ -118,6 +118,7 @@ export const TemplateForm = ({ name: '', indexPatterns: [], dataStream: {}, + indexMode: STANDARD_INDEX_MODE, template: {}, _kbnMeta: { type: 'default', @@ -341,7 +342,10 @@ export const TemplateForm = ({ )} - + diff --git a/x-pack/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx b/x-pack/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx index 5448c932d65e0..2695da7adb813 100644 --- a/x-pack/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx +++ b/x-pack/plugins/index_management/public/application/components/template_form/template_form_schemas.tsx @@ -23,6 +23,7 @@ import { allowAutoCreateRadioIds, INVALID_INDEX_PATTERN_CHARS, INVALID_TEMPLATE_NAME_CHARS, + STANDARD_INDEX_MODE, } from '../../../../common/constants'; const { @@ -150,6 +151,13 @@ export const schemas: Record = { }), defaultValue: false, }, + indexMode: { + type: FIELD_TYPES.SUPER_SELECT, + defaultValue: STANDARD_INDEX_MODE, + label: i18n.translate('xpack.idxMgmt.templateForm.stepLogistics.fieldIndexModeLabel', { + defaultMessage: 'Index mode', + }), + }, order: { type: FIELD_TYPES.NUMBER, label: i18n.translate('xpack.idxMgmt.templateForm.stepLogistics.fieldOrderLabel', { diff --git a/x-pack/plugins/index_management/public/application/lib/index_mode_labels.ts b/x-pack/plugins/index_management/public/application/lib/index_mode_labels.ts index 409659b8133c3..dd7a712567e9f 100644 --- a/x-pack/plugins/index_management/public/application/lib/index_mode_labels.ts +++ b/x-pack/plugins/index_management/public/application/lib/index_mode_labels.ts @@ -6,24 +6,43 @@ */ import { i18n } from '@kbn/i18n'; +import { + STANDARD_INDEX_MODE, + LOGSDB_INDEX_MODE, + TIME_SERIES_MODE, +} from '../../../common/constants'; -export const getIndexModeLabel = (mode?: string | null) => { - switch (mode) { - case 'standard': - case null: - case undefined: - return i18n.translate('xpack.idxMgmt.indexModeLabels.standardModeLabel', { - defaultMessage: 'Standard', - }); - case 'logsdb': - return i18n.translate('xpack.idxMgmt.indexModeLabels.logsdbModeLabel', { - defaultMessage: 'LogsDB', - }); - case 'time_series': - return i18n.translate('xpack.idxMgmt.indexModeLabels.tsdbModeLabel', { - defaultMessage: 'Time series', - }); - default: - return mode; - } +export const indexModeLabels = { + [STANDARD_INDEX_MODE]: i18n.translate('xpack.idxMgmt.indexModeLabels.standardIndexModeLabel', { + defaultMessage: 'Standard', + }), + [LOGSDB_INDEX_MODE]: i18n.translate('xpack.idxMgmt.indexModeLabels.logsdbIndexModeLabel', { + defaultMessage: 'LogsDB', + }), + [TIME_SERIES_MODE]: i18n.translate('xpack.idxMgmt.indexModeLabels.timeSeriesIndexModeLabel', { + defaultMessage: 'Time series', + }), +}; + +export const indexModeDescriptions = { + [STANDARD_INDEX_MODE]: i18n.translate( + 'xpack.idxMgmt.indexModeDescriptions.standardIndexModeDescription', + { + defaultMessage: + 'Standard indexing with default settings, for data other than logs or metrics', + } + ), + [LOGSDB_INDEX_MODE]: i18n.translate( + 'xpack.idxMgmt.indexModeDescriptions.logsdbIndexModeDescription', + { + defaultMessage: + 'Optimized for storing logs data, with reduced storage and default settings that help reduce the chance of logs being rejected by Elasticsearch', + } + ), + [TIME_SERIES_MODE]: i18n.translate( + 'xpack.idxMgmt.indexModeDescriptions.timeSeriesIndexModeDescription', + { + defaultMessage: 'Optimized for metrics data with reduced storage', + } + ), }; diff --git a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_detail_panel.tsx b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_detail_panel.tsx index 5b3bf0920c3b7..d962305a7147c 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_detail_panel.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_detail_panel/data_stream_detail_panel.tsx @@ -34,7 +34,7 @@ import { EuiSpacer, } from '@elastic/eui'; -import { getIndexModeLabel } from '../../../../lib/index_mode_labels'; +import { indexModeLabels } from '../../../../lib/index_mode_labels'; import { DiscoverLink } from '../../../../lib/discover_link'; import { getLifecycleValue } from '../../../../lib/data_streams'; import { SectionLoading, reactRouterNavigate } from '../../../../../shared_imports'; @@ -355,7 +355,7 @@ export const DataStreamDetailPanel: React.FunctionComponent = ({ defaultMessage: "The index mode applied to the data stream's backing indices, as defined in its associated index template.", }), - content: getIndexModeLabel(indexMode), + content: indexModeLabels[indexMode], dataTestSubj: 'indexModeDetail', }, { diff --git a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx index e91fd644f795c..59daae719bf47 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_table/data_stream_table.tsx @@ -36,7 +36,7 @@ import { humanizeTimeStamp } from '../humanize_time_stamp'; import { DataStreamsBadges } from '../data_stream_badges'; import { ConditionalWrap } from '../data_stream_detail_panel'; import { isDataStreamFullyManagedByILM } from '../../../../lib/data_streams'; -import { getIndexModeLabel } from '../../../../lib/index_mode_labels'; +import { indexModeLabels } from '../../../../lib/index_mode_labels'; import { FilterListButton, Filters } from '../../components'; import { type DataStreamFilterName } from '../data_stream_list'; @@ -192,7 +192,7 @@ export const DataStreamTable: React.FunctionComponent = ({ }), truncateText: true, sortable: true, - render: (indexMode: DataStream['indexMode']) => getIndexModeLabel(indexMode), + render: (indexMode: DataStream['indexMode']) => indexModeLabels[indexMode], }); columns.push({ diff --git a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx index ff06a08014f61..2621f3ec483c1 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/template_list/template_details/tabs/tab_summary.tsx @@ -28,7 +28,7 @@ import { TemplateDeserialized } from '../../../../../../../common'; import { ILM_PAGES_POLICY_EDIT } from '../../../../../constants'; import { useIlmLocator } from '../../../../../services/use_ilm_locator'; import { allowAutoCreateRadioIds } from '../../../../../../../common/constants'; -import { getIndexModeLabel } from '../../../../../lib/index_mode_labels'; +import { indexModeLabels } from '../../../../../lib/index_mode_labels'; interface Props { templateDetails: TemplateDeserialized; @@ -54,11 +54,11 @@ export const TabSummary: React.FunctionComponent = ({ templateDetails }) composedOf, order, indexPatterns = [], + indexMode, ilmPolicy, _meta, _kbnMeta: { isLegacy, hasDatastream }, allowAutoCreate, - template, } = templateDetails; const numIndexPatterns = indexPatterns.length; @@ -231,7 +231,7 @@ export const TabSummary: React.FunctionComponent = ({ templateDetails }) /> - {getIndexModeLabel(template?.settings?.index?.mode)} + {indexModeLabels[indexMode]} {/* Allow auto create */} diff --git a/x-pack/plugins/index_management/server/routes/api/templates/validate_schemas.ts b/x-pack/plugins/index_management/server/routes/api/templates/validate_schemas.ts index 97cb0a1c1c69c..cfd9c1d18e610 100644 --- a/x-pack/plugins/index_management/server/routes/api/templates/validate_schemas.ts +++ b/x-pack/plugins/index_management/server/routes/api/templates/validate_schemas.ts @@ -13,6 +13,7 @@ export const templateSchema = schema.object({ version: schema.maybe(schema.number()), order: schema.maybe(schema.number()), priority: schema.maybe(schema.number()), + indexMode: schema.string(), // Not present for legacy templates allowAutoCreate: schema.maybe(schema.string()), template: schema.maybe( diff --git a/x-pack/plugins/index_management/test/fixtures/template.ts b/x-pack/plugins/index_management/test/fixtures/template.ts index 54df4410352b6..09895b550dc18 100644 --- a/x-pack/plugins/index_management/test/fixtures/template.ts +++ b/x-pack/plugins/index_management/test/fixtures/template.ts @@ -23,6 +23,7 @@ export const getComposableTemplate = ({ isLegacy = false, type = 'default', allowAutoCreate = 'NO_OVERWRITE', + indexMode = 'standard', composedOf = [], }: Partial< TemplateDeserialized & { @@ -42,6 +43,7 @@ export const getComposableTemplate = ({ version, priority, indexPatterns, + indexMode, allowAutoCreate, template: { aliases, @@ -66,6 +68,7 @@ export const getTemplate = ({ order = getRandomNumber(), indexPatterns = [], template: { settings, aliases, mappings } = {}, + indexMode = 'standard', dataStream, composedOf, ignoreMissingComponentTemplates, @@ -85,6 +88,7 @@ export const getTemplate = ({ version, order, indexPatterns, + indexMode, allowAutoCreate, template: { aliases, diff --git a/x-pack/plugins/kubernetes_security/server/routes/aggregate.ts b/x-pack/plugins/kubernetes_security/server/routes/aggregate.ts index f83ddc818cbb4..4ddb828b68976 100644 --- a/x-pack/plugins/kubernetes_security/server/routes/aggregate.ts +++ b/x-pack/plugins/kubernetes_security/server/routes/aggregate.ts @@ -38,6 +38,11 @@ export const registerAggregateRoute = (router: IRouter, logger: Logger) => { .addVersion( { version: '1', + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, validate: { request: { query: schema.object({ diff --git a/x-pack/plugins/kubernetes_security/server/routes/count.ts b/x-pack/plugins/kubernetes_security/server/routes/count.ts index 0922adeb0cf45..b73452e8e45fc 100644 --- a/x-pack/plugins/kubernetes_security/server/routes/count.ts +++ b/x-pack/plugins/kubernetes_security/server/routes/count.ts @@ -28,6 +28,11 @@ export const registerCountRoute = (router: IRouter, logger: Logger) => { .addVersion( { version: '1', + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, validate: { request: { query: schema.object({ diff --git a/x-pack/plugins/kubernetes_security/server/routes/multi_terms_aggregate.ts b/x-pack/plugins/kubernetes_security/server/routes/multi_terms_aggregate.ts index 83f5b70efe051..b4a0271b63edc 100644 --- a/x-pack/plugins/kubernetes_security/server/routes/multi_terms_aggregate.ts +++ b/x-pack/plugins/kubernetes_security/server/routes/multi_terms_aggregate.ts @@ -35,6 +35,11 @@ export const registerMultiTermsAggregateRoute = (router: IRouter, logger: Logger .addVersion( { version: '1', + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, validate: { request: { query: schema.object({ diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/config.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/config.ts index 4df8891bd06fc..2f36d27889c14 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/config.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/config.ts @@ -9,7 +9,6 @@ import { schema, type TypeOf } from '@kbn/config-schema'; export const config = schema.object({ enabled: schema.boolean({ defaultValue: true }), - modelId: schema.maybe(schema.string()), // TODO: Remove scope: schema.maybe(schema.oneOf([schema.literal('observability'), schema.literal('search')])), enableKnowledgeBase: schema.boolean({ defaultValue: true }), }); diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/plugin.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/plugin.ts index 98a6232563054..b2b5736fd1d6f 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/plugin.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/plugin.ts @@ -116,11 +116,6 @@ export class ObservabilityAIAssistantPlugin // Using once to make sure the same model ID is used during service init and Knowledge base setup const getSearchConnectorModelId = once(async () => { - // TODO: Remove this once the modelId is removed from the config - const configModelId = this.config.modelId; - if (configModelId) { - return configModelId; - } const defaultModelId = '.elser_model_2'; const [_, pluginsStart] = await core.getStartServices(); // Wait for the license to be available so the ML plugin's guards pass once we ask for ELSER stats diff --git a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/inference_endpoint.ts b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/inference_endpoint.ts index 1d09311dbd6ea..e89028652d9ac 100644 --- a/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/inference_endpoint.ts +++ b/x-pack/plugins/observability_solution/observability_ai_assistant/server/service/inference_endpoint.ts @@ -10,7 +10,7 @@ import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { Logger } from '@kbn/logging'; import moment from 'moment'; -export const AI_ASSISTANT_KB_INFERENCE_ID = 'ai_assistant_kb_inference'; +export const AI_ASSISTANT_KB_INFERENCE_ID = 'obs_ai_assistant_kb_inference'; export async function createInferenceEndpoint({ esClient, diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/bulk_cruds/edit_monitor_bulk.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/bulk_cruds/edit_monitor_bulk.ts index 833e0203e3817..f106dc46c0708 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/bulk_cruds/edit_monitor_bulk.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/bulk_cruds/edit_monitor_bulk.ts @@ -28,7 +28,6 @@ import { export interface MonitorConfigUpdate { normalizedMonitor: SyntheticsMonitor; monitorWithRevision: SyntheticsMonitorWithSecretsAttributes; - previousMonitor: SavedObject; decryptedPreviousMonitor: SavedObject; } @@ -40,14 +39,14 @@ const updateConfigSavedObjects = async ({ monitorsToUpdate: MonitorConfigUpdate[]; }) => { return await routeContext.savedObjectsClient.bulkUpdate( - monitorsToUpdate.map(({ previousMonitor, monitorWithRevision }) => ({ + monitorsToUpdate.map(({ monitorWithRevision, decryptedPreviousMonitor }) => ({ type: syntheticsMonitorType, - id: previousMonitor.id, + id: decryptedPreviousMonitor.id, attributes: { ...monitorWithRevision, - [ConfigKey.CONFIG_ID]: previousMonitor.id, + [ConfigKey.CONFIG_ID]: decryptedPreviousMonitor.id, [ConfigKey.MONITOR_QUERY_ID]: - monitorWithRevision[ConfigKey.CUSTOM_HEARTBEAT_ID] || previousMonitor.id, + monitorWithRevision[ConfigKey.CUSTOM_HEARTBEAT_ID] || decryptedPreviousMonitor.id, }, })) ); @@ -67,15 +66,14 @@ async function syncUpdatedMonitors({ const { syntheticsMonitorClient } = routeContext; return await syntheticsMonitorClient.editMonitors( - monitorsToUpdate.map(({ normalizedMonitor, previousMonitor, decryptedPreviousMonitor }) => ({ + monitorsToUpdate.map(({ normalizedMonitor, decryptedPreviousMonitor }) => ({ monitor: { ...(normalizedMonitor as MonitorFields), - [ConfigKey.CONFIG_ID]: previousMonitor.id, + [ConfigKey.CONFIG_ID]: decryptedPreviousMonitor.id, [ConfigKey.MONITOR_QUERY_ID]: - normalizedMonitor[ConfigKey.CUSTOM_HEARTBEAT_ID] || previousMonitor.id, + normalizedMonitor[ConfigKey.CUSTOM_HEARTBEAT_ID] || decryptedPreviousMonitor.id, }, - id: previousMonitor.id, - previousMonitor, + id: decryptedPreviousMonitor.id, decryptedPreviousMonitor, })), privateLocations, @@ -104,9 +102,9 @@ export const syncEditedMonitorBulk = async ({ const { failedPolicyUpdates, publicSyncErrors } = editSyncResponse; - monitorsToUpdate.forEach(({ normalizedMonitor, previousMonitor }) => { + monitorsToUpdate.forEach(({ normalizedMonitor, decryptedPreviousMonitor }) => { const editedMonitorSavedObject = editedMonitorSavedObjects?.saved_objects.find( - (obj) => obj.id === previousMonitor.id + (obj) => obj.id === decryptedPreviousMonitor.id ); sendTelemetryEvents( @@ -114,7 +112,7 @@ export const syncEditedMonitorBulk = async ({ server.telemetry, formatTelemetryUpdateEvent( editedMonitorSavedObject as SavedObjectsUpdateResponse, - previousMonitor, + decryptedPreviousMonitor.updated_at, server.stackVersion, Boolean((normalizedMonitor as MonitorFields)[ConfigKey.SOURCE_INLINE]), publicSyncErrors @@ -150,9 +148,9 @@ export const rollbackCompletely = async ({ const { savedObjectsClient, server } = routeContext; try { await savedObjectsClient.bulkUpdate( - monitorsToUpdate.map(({ previousMonitor, decryptedPreviousMonitor }) => ({ + monitorsToUpdate.map(({ decryptedPreviousMonitor }) => ({ type: syntheticsMonitorType, - id: previousMonitor.id, + id: decryptedPreviousMonitor.id, attributes: decryptedPreviousMonitor.attributes, })) ); @@ -167,7 +165,6 @@ export const rollbackFailedUpdates = async ({ monitorsToUpdate, }: { monitorsToUpdate: Array<{ - previousMonitor: SavedObject; decryptedPreviousMonitor: SavedObject; }>; routeContext: RouteContext; @@ -194,12 +191,12 @@ export const rollbackFailedUpdates = async ({ }); const monitorsToRevert = monitorsToUpdate - .filter(({ previousMonitor }) => { - return failedConfigs[previousMonitor.id]; + .filter(({ decryptedPreviousMonitor }) => { + return failedConfigs[decryptedPreviousMonitor.id]; }) - .map(({ previousMonitor, decryptedPreviousMonitor }) => ({ + .map(({ decryptedPreviousMonitor }) => ({ type: syntheticsMonitorType, - id: previousMonitor.id, + id: decryptedPreviousMonitor.id, attributes: decryptedPreviousMonitor.attributes, })); diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/edit_monitor.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/edit_monitor.ts index d9261847db1ab..d460b71037950 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/edit_monitor.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/edit_monitor.ts @@ -272,7 +272,7 @@ export const syncEditedMonitor = async ({ server.telemetry, formatTelemetryUpdateEvent( editedMonitorSavedObject as SavedObjectsUpdateResponse, - decryptedPreviousMonitor, + decryptedPreviousMonitor.updated_at, server.stackVersion, Boolean((normalizedMonitor as MonitorFields)[ConfigKey.SOURCE_INLINE]), publicSyncErrors diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/get_monitor_project.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/get_monitor_project.ts index b0b4eee392d56..ac55807f9412f 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/get_monitor_project.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/routes/monitor_cruds/get_monitor_project.ts @@ -32,7 +32,7 @@ export const getSyntheticsProjectMonitorsRoute: SyntheticsRestApiRouteFactory = } = routeContext; const { projectName } = request.params; - const { per_page: perPage = 500, search_after: searchAfter } = request.query; + const { per_page: perPage = 1000, search_after: searchAfter } = request.query; const decodedProjectName = decodeURI(projectName); const decodedSearchAfter = searchAfter ? decodeURI(searchAfter) : undefined; diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/telemetry/monitor_upgrade_sender.test.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/telemetry/monitor_upgrade_sender.test.ts index 299ab62f09028..761472bb7fa8c 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/routes/telemetry/monitor_upgrade_sender.test.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/routes/telemetry/monitor_upgrade_sender.test.ts @@ -152,7 +152,7 @@ describe('monitor upgrade telemetry helpers', () => { it('handles formatting update events', () => { const actual = formatTelemetryUpdateEvent( createTestConfig({}, '2011-10-05T16:48:00.000Z'), - testConfig, + testConfig.updated_at, stackVersion, false, errors diff --git a/x-pack/plugins/observability_solution/synthetics/server/routes/telemetry/monitor_upgrade_sender.ts b/x-pack/plugins/observability_solution/synthetics/server/routes/telemetry/monitor_upgrade_sender.ts index f008d00a18536..f000108620f45 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/routes/telemetry/monitor_upgrade_sender.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/routes/telemetry/monitor_upgrade_sender.ts @@ -109,23 +109,22 @@ export function formatTelemetryEvent({ export function formatTelemetryUpdateEvent( currentMonitor: SavedObjectsUpdateResponse, - previousMonitor: SavedObject, + previousMonitorUpdatedAt: string | undefined, stackVersion: string, isInlineScript: boolean, errors?: ServiceLocationErrors | null ) { let durationSinceLastUpdated: number = 0; - if (currentMonitor.updated_at && previousMonitor.updated_at) { + if (currentMonitor.updated_at && previousMonitorUpdatedAt) { durationSinceLastUpdated = - new Date(currentMonitor.updated_at).getTime() - - new Date(previousMonitor.updated_at).getTime(); + new Date(currentMonitor.updated_at).getTime() - new Date(previousMonitorUpdatedAt).getTime(); } return formatTelemetryEvent({ stackVersion, monitor: currentMonitor as SavedObject, durationSinceLastUpdated, - lastUpdatedAt: previousMonitor.updated_at, + lastUpdatedAt: previousMonitorUpdatedAt, isInlineScript, errors, }); diff --git a/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.ts b/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.ts index 6069076e375a2..1fe5d30048418 100644 --- a/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.ts +++ b/x-pack/plugins/observability_solution/synthetics/server/synthetics_service/project_monitor/project_monitor_formatter.ts @@ -45,6 +45,17 @@ import { normalizeProjectMonitor } from './normalizers'; type FailedError = Array<{ id?: string; reason: string; details: string; payload?: object }>; +export interface ExistingMonitor { + [ConfigKey.JOURNEY_ID]: string; + [ConfigKey.CONFIG_ID]: string; + [ConfigKey.REVISION]: number; + [ConfigKey.MONITOR_TYPE]: string; +} + +export interface PreviousMonitorForUpdate extends ExistingMonitor { + updated_at?: string; +} + export const CANNOT_UPDATE_MONITOR_TO_DIFFERENT_TYPE = i18n.translate( 'xpack.synthetics.service.projectMonitors.cannotUpdateMonitorToDifferentType', { @@ -128,14 +139,13 @@ export class ProjectMonitorFormatter { const normalizedNewMonitors: SyntheticsMonitor[] = []; const normalizedUpdateMonitors: Array<{ - previousMonitor: SavedObjectsFindResult; + previousMonitor: PreviousMonitorForUpdate; monitor: SyntheticsMonitor; }> = []; for (const monitor of this.monitors) { const previousMonitor = existingMonitors.find( - (monitorObj) => - (monitorObj.attributes as SyntheticsMonitor)[ConfigKey.JOURNEY_ID] === monitor.id + (monitorObj) => monitorObj[ConfigKey.JOURNEY_ID] === monitor.id ); const normM = await this.validateProjectMonitor({ @@ -146,7 +156,7 @@ export class ProjectMonitorFormatter { if (normM) { if ( previousMonitor && - previousMonitor.attributes[ConfigKey.MONITOR_TYPE] !== normM[ConfigKey.MONITOR_TYPE] + previousMonitor[ConfigKey.MONITOR_TYPE] !== normM[ConfigKey.MONITOR_TYPE] ) { this.failedMonitors.push({ reason: CANNOT_UPDATE_MONITOR_TO_DIFFERENT_TYPE, @@ -157,7 +167,7 @@ export class ProjectMonitorFormatter { 'Monitor {monitorId} of type {previousType} cannot be updated to type {currentType}. Please delete the monitor first and try again.', values: { currentType: monitor.type, - previousType: previousMonitor.attributes[ConfigKey.MONITOR_TYPE], + previousType: previousMonitor[ConfigKey.MONITOR_TYPE], monitorId: monitor.id, }, } @@ -246,19 +256,33 @@ export class ProjectMonitorFormatter { } }; - public getProjectMonitorsForProject = async () => { - const finder = this.savedObjectsClient.createPointInTimeFinder({ + public getProjectMonitorsForProject = async (): Promise => { + const journeyIds = this.monitors.map((monitor) => monitor.id); + const journeyFilter = getSavedObjectKqlFilter({ + field: ConfigKey.JOURNEY_ID, + values: journeyIds, + }); + const finder = this.savedObjectsClient.createPointInTimeFinder({ type: syntheticsMonitorType, perPage: 5000, - filter: this.projectFilter, + filter: `${this.projectFilter} AND ${journeyFilter}`, + fields: [ + ConfigKey.JOURNEY_ID, + ConfigKey.CONFIG_ID, + ConfigKey.REVISION, + ConfigKey.MONITOR_TYPE, + ], }); - const hits: Array> = []; + const hits: PreviousMonitorForUpdate[] = []; for await (const result of finder.find()) { hits.push( - ...(result.saved_objects as Array< - SavedObjectsFindResult - >) + ...result.saved_objects.map((monitor) => { + return { + ...monitor.attributes, + updated_at: monitor.updated_at, + }; + }) ); } @@ -333,10 +357,8 @@ export class ProjectMonitorFormatter { } }; - private getDecryptedMonitors = async ( - monitors: Array> - ) => { - const configIds = monitors.map((monitor) => monitor.attributes[ConfigKey.CONFIG_ID]); + private getDecryptedMonitors = async (monitors: PreviousMonitorForUpdate[]) => { + const configIds = monitors.map((monitor) => monitor[ConfigKey.CONFIG_ID]); const monitorFilter = getSavedObjectKqlFilter({ field: ConfigKey.CONFIG_ID, values: configIds, @@ -344,7 +366,7 @@ export class ProjectMonitorFormatter { const finder = await this.encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser( { - search: monitorFilter, + filter: monitorFilter, type: syntheticsMonitorType, perPage: 500, namespaces: [this.spaceId], @@ -365,7 +387,7 @@ export class ProjectMonitorFormatter { private updateMonitorsBulk = async ( monitors: Array<{ monitor: SyntheticsMonitor; - previousMonitor: SavedObjectsFindResult; + previousMonitor: PreviousMonitorForUpdate; }> ): Promise< | { @@ -390,10 +412,11 @@ export class ProjectMonitorFormatter { const monitorsToUpdate: MonitorConfigUpdate[] = []; decryptedPreviousMonitors.forEach((decryptedPreviousMonitor) => { - const monitor = monitors.find((m) => m.previousMonitor.id === decryptedPreviousMonitor.id); + const monitor = monitors.find( + (m) => m.previousMonitor[ConfigKey.CONFIG_ID] === decryptedPreviousMonitor.id + ); if (monitor) { const normalizedMonitor = monitor?.monitor; - const previousMonitor = monitor?.previousMonitor; const { attributes: { [ConfigKey.REVISION]: _, ...normalizedPrevMonitorAttr }, } = normalizeSecrets(decryptedPreviousMonitor); @@ -401,11 +424,10 @@ export class ProjectMonitorFormatter { const monitorWithRevision = formatSecrets({ ...normalizedPrevMonitorAttr, ...normalizedMonitor, - revision: (previousMonitor.attributes[ConfigKey.REVISION] || 0) + 1, + revision: (decryptedPreviousMonitor.attributes[ConfigKey.REVISION] || 0) + 1, }); monitorsToUpdate.push({ normalizedMonitor, - previousMonitor, monitorWithRevision, decryptedPreviousMonitor, }); diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx index e0199ab40168d..fff9450b6a1cb 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.test.tsx @@ -11,6 +11,9 @@ import { AlertsPreview } from './alerts_preview'; import { TestProviders } from '../../../common/mock/test_providers'; import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; import type { ParsedAlertsData } from '../../../overview/components/detection_response/alerts_by_status/types'; +import { useMisconfigurationPreview } from '@kbn/cloud-security-posture/src/hooks/use_misconfiguration_preview'; +import { useVulnerabilitiesPreview } from '@kbn/cloud-security-posture/src/hooks/use_vulnerabilities_preview'; +import { useRiskScore } from '../../../entity_analytics/api/hooks/use_risk_score'; const mockAlertsData: ParsedAlertsData = { open: { @@ -29,9 +32,10 @@ const mockAlertsData: ParsedAlertsData = { }, }; -jest.mock( - '../../../detections/components/alerts_kpis/alerts_summary_charts_panel/use_summary_chart_data' -); +// Mock hooks +jest.mock('@kbn/cloud-security-posture/src/hooks/use_misconfiguration_preview'); +jest.mock('@kbn/cloud-security-posture/src/hooks/use_vulnerabilities_preview'); +jest.mock('../../../entity_analytics/api/hooks/use_risk_score'); jest.mock('@kbn/expandable-flyout'); describe('AlertsPreview', () => { @@ -39,6 +43,13 @@ describe('AlertsPreview', () => { beforeEach(() => { (useExpandableFlyoutApi as jest.Mock).mockReturnValue({ openLeftPanel: mockOpenLeftPanel }); + (useVulnerabilitiesPreview as jest.Mock).mockReturnValue({ + data: { count: { CRITICAL: 0, HIGH: 1, MEDIUM: 1, LOW: 0, UNKNOWN: 0 } }, + }); + (useRiskScore as jest.Mock).mockReturnValue({ data: [{ host: { risk: 75 } }] }); + (useMisconfigurationPreview as jest.Mock).mockReturnValue({ + data: { count: { passed: 1, failed: 1 } }, + }); }); afterEach(() => { jest.clearAllMocks(); @@ -47,17 +58,17 @@ describe('AlertsPreview', () => { it('renders', () => { const { getByTestId } = render( - + ); - expect(getByTestId('securitySolutionFlyoutInsightsAlertsTitleText')).toBeInTheDocument(); + expect(getByTestId('securitySolutionFlyoutInsightsAlertsTitleLink')).toBeInTheDocument(); }); it('renders correct alerts number', () => { const { getByTestId } = render( - + ); @@ -67,7 +78,7 @@ describe('AlertsPreview', () => { it('should render the correct number of distribution bar section based on the number of severities', () => { const { queryAllByTestId } = render( - + ); diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx index 3f9a0115d9ed1..c832f12c93f78 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/alerts/alerts_preview.tsx @@ -5,19 +5,40 @@ * 2.0. */ -import React from 'react'; +import React, { useCallback, useMemo } from 'react'; import { capitalize } from 'lodash'; import type { EuiThemeComputed } from '@elastic/eui'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText, EuiTitle, useEuiTheme } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { DistributionBar } from '@kbn/security-solution-distribution-bar'; -import { getAbbreviatedNumber } from '@kbn/cloud-security-posture-common'; -import { ExpandablePanel } from '../../../flyout/shared/components/expandable_panel'; -import { getSeverityColor } from '../../../detections/components/alerts_kpis/severity_level_panel/helpers'; +import { + buildEntityFlyoutPreviewQuery, + getAbbreviatedNumber, +} from '@kbn/cloud-security-posture-common'; +import { hasVulnerabilitiesData } from '@kbn/cloud-security-posture'; +import { useMisconfigurationPreview } from '@kbn/cloud-security-posture/src/hooks/use_misconfiguration_preview'; +import { useVulnerabilitiesPreview } from '@kbn/cloud-security-posture/src/hooks/use_vulnerabilities_preview'; +import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; import type { AlertsByStatus, ParsedAlertsData, } from '../../../overview/components/detection_response/alerts_by_status/types'; +import { ExpandablePanel } from '../../../flyout/shared/components/expandable_panel'; +import { getSeverityColor } from '../../../detections/components/alerts_kpis/severity_level_panel/helpers'; +import type { HostRiskScore, UserRiskScore } from '../../../../common/search_strategy'; +import { + buildHostNamesFilter, + buildUserNamesFilter, + RiskScoreEntity, +} from '../../../../common/search_strategy'; +import { useRiskScore } from '../../../entity_analytics/api/hooks/use_risk_score'; +import { FIRST_RECORD_PAGINATION } from '../../../entity_analytics/common'; +import { HostDetailsPanelKey } from '../../../flyout/entity_details/host_details_left'; +import { + EntityDetailsLeftPanelTab, + CspInsightLeftPanelSubTab, +} from '../../../flyout/entity_details/shared/components/left_panel/left_panel_header'; +import { UserDetailsPanelKey } from '../../../flyout/entity_details/user_details_left'; const AlertsCount = ({ alertsTotal, @@ -56,9 +77,13 @@ const AlertsCount = ({ export const AlertsPreview = ({ alertsData, + fieldName, + name, isPreviewMode, }: { alertsData: ParsedAlertsData; + fieldName: string; + name: string; isPreviewMode?: boolean; }) => { const { euiTheme } = useEuiTheme(); @@ -82,9 +107,120 @@ export const AlertsPreview = ({ const totalAlertsCount = alertStats.reduce((total, item) => total + item.count, 0); + const { data } = useMisconfigurationPreview({ + query: buildEntityFlyoutPreviewQuery(fieldName, name), + sort: [], + enabled: true, + pageSize: 1, + ignore_unavailable: true, + }); + const isUsingHostName = fieldName === 'host.name'; + const passedFindings = data?.count.passed || 0; + const failedFindings = data?.count.failed || 0; + + const hasMisconfigurationFindings = passedFindings > 0 || failedFindings > 0; + + const { data: vulnerabilitiesData } = useVulnerabilitiesPreview({ + query: buildEntityFlyoutPreviewQuery('host.name', name), + sort: [], + enabled: true, + pageSize: 1, + }); + + const { + CRITICAL = 0, + HIGH = 0, + MEDIUM = 0, + LOW = 0, + NONE = 0, + } = vulnerabilitiesData?.count || {}; + + const hasVulnerabilitiesFindings = hasVulnerabilitiesData({ + critical: CRITICAL, + high: HIGH, + medium: MEDIUM, + low: LOW, + none: NONE, + }); + + const buildFilterQuery = useMemo( + () => (isUsingHostName ? buildHostNamesFilter([name]) : buildUserNamesFilter([name])), + [isUsingHostName, name] + ); + + const riskScoreState = useRiskScore({ + riskEntity: isUsingHostName ? RiskScoreEntity.host : RiskScoreEntity.user, + filterQuery: buildFilterQuery, + onlyLatest: false, + pagination: FIRST_RECORD_PAGINATION, + }); + + const { data: hostRisk } = riskScoreState; + + const riskData = hostRisk?.[0]; + + const isRiskScoreExist = isUsingHostName + ? !!(riskData as HostRiskScore)?.host.risk + : !!(riskData as UserRiskScore)?.user.risk; + + const hasNonClosedAlerts = totalAlertsCount > 0; + + const { openLeftPanel } = useExpandableFlyoutApi(); + + const goToEntityInsightTab = useCallback(() => { + openLeftPanel({ + id: isUsingHostName ? HostDetailsPanelKey : UserDetailsPanelKey, + params: isUsingHostName + ? { + name, + isRiskScoreExist, + hasMisconfigurationFindings, + hasVulnerabilitiesFindings, + hasNonClosedAlerts, + path: { + tab: EntityDetailsLeftPanelTab.CSP_INSIGHTS, + subTab: CspInsightLeftPanelSubTab.ALERTS, + }, + } + : { + user: { name }, + isRiskScoreExist, + hasMisconfigurationFindings, + hasNonClosedAlerts, + path: { + tab: EntityDetailsLeftPanelTab.CSP_INSIGHTS, + subTab: CspInsightLeftPanelSubTab.ALERTS, + }, + }, + }); + }, [ + hasMisconfigurationFindings, + hasNonClosedAlerts, + hasVulnerabilitiesFindings, + isRiskScoreExist, + isUsingHostName, + name, + openLeftPanel, + ]); + const link = useMemo( + () => + !isPreviewMode + ? { + callback: goToEntityInsightTab, + tooltip: ( + + ), + } + : undefined, + [isPreviewMode, goToEntityInsightTab] + ); return ( ), + link: totalAlertsCount > 0 ? link : undefined, }} data-test-subj={'securitySolutionFlyoutInsightsAlerts'} > diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx new file mode 100644 index 0000000000000..966de68e3497f --- /dev/null +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/alerts_findings_details_table.tsx @@ -0,0 +1,265 @@ +/* + * 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. + */ + +import React, { memo, useCallback, useEffect, useState } from 'react'; +import { capitalize } from 'lodash'; +import type { Criteria, EuiBasicTableColumn } from '@elastic/eui'; +import { EuiSpacer, EuiPanel, EuiText, EuiBasicTable, EuiIcon, EuiLink } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { DistributionBar } from '@kbn/security-solution-distribution-bar'; +import { + ENTITY_FLYOUT_EXPAND_MISCONFIGURATION_VIEW_VISITS, + uiMetricService, +} from '@kbn/cloud-security-posture-common/utils/ui_metrics'; +import { METRIC_TYPE } from '@kbn/analytics'; +import { buildEntityAlertsQuery } from '@kbn/cloud-security-posture-common/utils/helpers'; +import { useExpandableFlyoutApi } from '@kbn/expandable-flyout'; +import { TableId } from '@kbn/securitysolution-data-table'; +import { + OPEN_IN_ALERTS_TITLE_HOSTNAME, + OPEN_IN_ALERTS_TITLE_STATUS, + OPEN_IN_ALERTS_TITLE_USERNAME, +} from '../../../overview/components/detection_response/translations'; +import { useNavigateToAlertsPageWithFilters } from '../../../common/hooks/use_navigate_to_alerts_page_with_filters'; +import { DocumentDetailsPreviewPanelKey } from '../../../flyout/document_details/shared/constants/panel_keys'; +import { useGlobalTime } from '../../../common/containers/use_global_time'; +import { useQueryAlerts } from '../../../detections/containers/detection_engine/alerts/use_query'; +import { ALERTS_QUERY_NAMES } from '../../../detections/containers/detection_engine/alerts/constants'; +import { useSignalIndex } from '../../../detections/containers/detection_engine/alerts/use_signal_index'; +import { getSeverityColor } from '../../../detections/components/alerts_kpis/severity_level_panel/helpers'; +import { SeverityBadge } from '../../../common/components/severity_badge'; +import { ALERT_PREVIEW_BANNER } from '../../../flyout/document_details/preview/constants'; +import { FILTER_OPEN, FILTER_ACKNOWLEDGED } from '../../../../common/types'; + +type AlertSeverity = 'low' | 'medium' | 'high' | 'critical'; + +interface ResultAlertsField { + _id: string[]; + _index: string[]; + 'kibana.alert.rule.uuid': string[]; + 'kibana.alert.severity': AlertSeverity[]; + 'kibana.alert.rule.name': string[]; + 'kibana.alert.workflow_status': string[]; +} + +interface ContextualFlyoutAlertsField { + id: string; + index: string; + ruleUuid: string; + ruleName: string; + severity: AlertSeverity; + status: string; +} + +interface AlertsDetailsFields { + fields: ResultAlertsField; +} + +export const AlertsDetailsTable = memo( + ({ fieldName, queryName }: { fieldName: 'host.name' | 'user.name'; queryName: string }) => { + useEffect(() => { + uiMetricService.trackUiMetric( + METRIC_TYPE.COUNT, + ENTITY_FLYOUT_EXPAND_MISCONFIGURATION_VIEW_VISITS + ); + }, []); + + const [pageIndex, setPageIndex] = useState(0); + const [pageSize, setPageSize] = useState(10); + + const alertsPagination = (alerts: ContextualFlyoutAlertsField[]) => { + let pageOfItems; + + if (!pageIndex && !pageSize) { + pageOfItems = alerts; + } else { + const startIndex = pageIndex * pageSize; + pageOfItems = alerts?.slice(startIndex, Math.min(startIndex + pageSize, alerts?.length)); + } + + return { + pageOfItems, + totalItemCount: alerts?.length, + }; + }; + + const { to, from } = useGlobalTime(); + const { signalIndexName } = useSignalIndex(); + const { data } = useQueryAlerts({ + query: buildEntityAlertsQuery(fieldName, to, from, queryName, 500), + queryName: ALERTS_QUERY_NAMES.BY_RULE_BY_STATUS, + indexName: signalIndexName, + }); + + const alertDataResults = (data?.hits?.hits as AlertsDetailsFields[])?.map( + (item: AlertsDetailsFields) => { + return { + id: item.fields?._id?.[0], + index: item.fields?._index?.[0], + ruleName: item.fields?.['kibana.alert.rule.name']?.[0], + ruleUuid: item.fields?.['kibana.alert.rule.uuid']?.[0], + severity: item.fields?.['kibana.alert.severity']?.[0], + status: item.fields?.['kibana.alert.workflow_status']?.[0], + }; + } + ); + + const severitiesMap = alertDataResults?.map((item) => item.severity) || []; + + const alertStats = Object.entries( + severitiesMap.reduce((acc: Record, item) => { + acc[item] = (acc[item] || 0) + 1; + return acc; + }, {}) + ).map(([key, count]) => ({ + key: capitalize(key), + count, + color: getSeverityColor(key), + })); + + const { pageOfItems, totalItemCount } = alertsPagination(alertDataResults || []); + + const pagination = { + pageIndex, + pageSize, + totalItemCount, + pageSizeOptions: [10, 25, 100], + }; + + const onTableChange = ({ page }: Criteria) => { + if (page) { + const { index, size } = page; + setPageIndex(index); + setPageSize(size); + } + }; + + const { openPreviewPanel } = useExpandableFlyoutApi(); + + const handleOnEventAlertDetailPanelOpened = useCallback( + (eventId: string, indexName: string, tableId: string) => { + openPreviewPanel({ + id: DocumentDetailsPreviewPanelKey, + params: { + id: eventId, + indexName, + scopeId: tableId, + isPreviewMode: true, + banner: ALERT_PREVIEW_BANNER, + }, + }); + }, + [openPreviewPanel] + ); + + const tableId = TableId.alertsOnRuleDetailsPage; + + const columns: Array> = [ + { + field: 'id', + name: '', + width: '5%', + render: (id: string, alert: ContextualFlyoutAlertsField) => ( + handleOnEventAlertDetailPanelOpened(id, alert.index, tableId)}> + + + ), + }, + { + field: 'ruleName', + render: (ruleName: string) => {ruleName}, + name: i18n.translate( + 'xpack.securitySolution.flyout.left.insights.alerts.table.ruleNameColumnName', + { + defaultMessage: 'Rule', + } + ), + width: '55%', + }, + { + field: 'severity', + render: (severity: AlertSeverity) => ( + + + + ), + name: i18n.translate( + 'xpack.securitySolution.flyout.left.insights.alerts.table.severityColumnName', + { + defaultMessage: 'Severity', + } + ), + width: '20%', + }, + { + field: 'status', + render: (status: string) => {capitalize(status)}, + name: i18n.translate( + 'xpack.securitySolution.flyout.left.insights.alerts.table.statusColumnName', + { + defaultMessage: 'Status', + } + ), + width: '20%', + }, + ]; + + const openAlertsPageWithFilters = useNavigateToAlertsPageWithFilters(); + + const openAlertsInAlertsPage = useCallback( + () => + openAlertsPageWithFilters( + [ + { + title: + fieldName === 'host.name' + ? OPEN_IN_ALERTS_TITLE_HOSTNAME + : OPEN_IN_ALERTS_TITLE_USERNAME, + selectedOptions: [queryName], + fieldName, + }, + { + title: OPEN_IN_ALERTS_TITLE_STATUS, + selectedOptions: [FILTER_OPEN, FILTER_ACKNOWLEDGED], + fieldName: 'kibana.alert.workflow_status', + }, + ], + true + ), + [fieldName, openAlertsPageWithFilters, queryName] + ); + + return ( + <> + + openAlertsInAlertsPage()}> +

+ {i18n.translate('xpack.securitySolution.flyout.left.insights.alerts.tableTitle', { + defaultMessage: 'Alerts ', + })} + +

+
+ + + + + +
+ + ); + } +); + +AlertsDetailsTable.displayName = 'AlertsDetailsTable'; diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/insights_tab_csp.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/insights_tab_csp.tsx index 05421cfa7a208..2e7b4171fd023 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/insights_tab_csp.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/csp_details/insights_tab_csp.tsx @@ -12,10 +12,10 @@ import { FormattedMessage } from '@kbn/i18n-react'; import type { FlyoutPanelProps, PanelPath } from '@kbn/expandable-flyout'; import { useExpandableFlyoutState } from '@kbn/expandable-flyout'; import { i18n } from '@kbn/i18n'; -// import type { FlyoutPanels } from '@kbn/expandable-flyout/src/store/state'; import { CspInsightLeftPanelSubTab } from '../../../flyout/entity_details/shared/components/left_panel/left_panel_header'; import { MisconfigurationFindingsDetailsTable } from './misconfiguration_findings_details_table'; import { VulnerabilitiesFindingsDetailsTable } from './vulnerabilities_findings_details_table'; +import { AlertsDetailsTable } from './alerts_findings_details_table'; /** * Insights view displayed in the document details expandable flyout left section @@ -26,6 +26,7 @@ interface CspFlyoutPanelProps extends FlyoutPanelProps { path: PanelPath; hasMisconfigurationFindings: boolean; hasVulnerabilitiesFindings: boolean; + hasNonClosedAlerts: boolean; }; } @@ -35,7 +36,8 @@ function isCspFlyoutPanelProps( ): panelLeft is CspFlyoutPanelProps { return ( !!panelLeft?.params?.hasMisconfigurationFindings || - !!panelLeft?.params?.hasVulnerabilitiesFindings + !!panelLeft?.params?.hasVulnerabilitiesFindings || + !!panelLeft?.params?.hasNonClosedAlerts ); } @@ -45,12 +47,14 @@ export const InsightsTabCsp = memo( let hasMisconfigurationFindings = false; let hasVulnerabilitiesFindings = false; + let hasNonClosedAlerts = false; let subTab: string | undefined; // Check if panels.left is of type CspFlyoutPanelProps and extract values if (isCspFlyoutPanelProps(panels.left)) { hasMisconfigurationFindings = panels.left.params.hasMisconfigurationFindings; hasVulnerabilitiesFindings = panels.left.params.hasVulnerabilitiesFindings; + hasNonClosedAlerts = panels.left.params.hasNonClosedAlerts; subTab = panels.left.params.path?.subTab; } @@ -63,6 +67,8 @@ export const InsightsTabCsp = memo( ? CspInsightLeftPanelSubTab.MISCONFIGURATIONS : hasVulnerabilitiesFindings ? CspInsightLeftPanelSubTab.VULNERABILITIES + : hasNonClosedAlerts + ? CspInsightLeftPanelSubTab.ALERTS : ''; }; @@ -71,6 +77,19 @@ export const InsightsTabCsp = memo( const insightsButtons: EuiButtonGroupOptionProps[] = useMemo(() => { const buttons: EuiButtonGroupOptionProps[] = []; + if (panels.left?.params?.hasNonClosedAlerts) { + buttons.push({ + id: CspInsightLeftPanelSubTab.ALERTS, + label: ( + + ), + 'data-test-subj': 'alertsTabDataTestId', + }); + } + if (panels.left?.params?.hasMisconfigurationFindings) { buttons.push({ id: CspInsightLeftPanelSubTab.MISCONFIGURATIONS, @@ -96,9 +115,11 @@ export const InsightsTabCsp = memo( 'data-test-subj': 'vulnerabilitiesTabDataTestId', }); } + return buttons; }, [ panels.left?.params?.hasMisconfigurationFindings, + panels.left?.params?.hasNonClosedAlerts, panels.left?.params?.hasVulnerabilitiesFindings, ]); @@ -130,8 +151,10 @@ export const InsightsTabCsp = memo( {activeInsightsId === CspInsightLeftPanelSubTab.MISCONFIGURATIONS ? ( - ) : ( + ) : activeInsightsId === CspInsightLeftPanelSubTab.VULNERABILITIES ? ( + ) : ( + )} ); diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx index a43b56876f1ab..7139994f7e972 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/entity_insight.tsx @@ -94,7 +94,12 @@ export const EntityInsight = ({ if (alertsCount > 0) { insightContent.push( <> - + ); @@ -103,14 +108,23 @@ export const EntityInsight = ({ if (hasMisconfigurationFindings) insightContent.push( <> - + 0} + isPreviewMode={isPreviewMode} + /> ); if (isVulnerabilitiesFindingForHost && hasVulnerabilitiesFindings) insightContent.push( <> - + 0} + /> ); diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/misconfiguration/misconfiguration_preview.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/misconfiguration/misconfiguration_preview.tsx index b133e9db22050..42a5906ce4e36 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/misconfiguration/misconfiguration_preview.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/misconfiguration/misconfiguration_preview.tsx @@ -103,10 +103,12 @@ const MisconfigurationPreviewScore = ({ export const MisconfigurationsPreview = ({ name, fieldName, + hasNonClosedAlerts = false, isPreviewMode, }: { name: string; fieldName: 'host.name' | 'user.name'; + hasNonClosedAlerts?: boolean; isPreviewMode?: boolean; }) => { const { data } = useMisconfigurationPreview({ @@ -180,6 +182,7 @@ export const MisconfigurationsPreview = ({ isRiskScoreExist, hasMisconfigurationFindings, hasVulnerabilitiesFindings, + hasNonClosedAlerts, path: { tab: EntityDetailsLeftPanelTab.CSP_INSIGHTS, subTab: CspInsightLeftPanelSubTab.MISCONFIGURATIONS, @@ -189,11 +192,16 @@ export const MisconfigurationsPreview = ({ user: { name }, isRiskScoreExist, hasMisconfigurationFindings, - path: { tab: EntityDetailsLeftPanelTab.CSP_INSIGHTS }, + hasNonClosedAlerts, + path: { + tab: EntityDetailsLeftPanelTab.CSP_INSIGHTS, + subTab: CspInsightLeftPanelSubTab.MISCONFIGURATIONS, + }, }, }); }, [ hasMisconfigurationFindings, + hasNonClosedAlerts, hasVulnerabilitiesFindings, isRiskScoreExist, isUsingHostName, diff --git a/x-pack/plugins/security_solution/public/cloud_security_posture/components/vulnerabilities/vulnerabilities_preview.tsx b/x-pack/plugins/security_solution/public/cloud_security_posture/components/vulnerabilities/vulnerabilities_preview.tsx index a9ddaff62085b..c4335d921e371 100644 --- a/x-pack/plugins/security_solution/public/cloud_security_posture/components/vulnerabilities/vulnerabilities_preview.tsx +++ b/x-pack/plugins/security_solution/public/cloud_security_posture/components/vulnerabilities/vulnerabilities_preview.tsx @@ -72,9 +72,11 @@ const VulnerabilitiesCount = ({ export const VulnerabilitiesPreview = ({ name, isPreviewMode, + hasNonClosedAlerts = false, }: { name: string; isPreviewMode?: boolean; + hasNonClosedAlerts?: boolean; }) => { useEffect(() => { uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, ENTITY_FLYOUT_WITH_VULNERABILITY_PREVIEW); @@ -132,11 +134,13 @@ export const VulnerabilitiesPreview = ({ isRiskScoreExist, hasMisconfigurationFindings, hasVulnerabilitiesFindings, + hasNonClosedAlerts, path: { tab: EntityDetailsLeftPanelTab.CSP_INSIGHTS, subTab: 'vulnerabilitiesTabId' }, }, }); }, [ hasMisconfigurationFindings, + hasNonClosedAlerts, hasVulnerabilitiesFindings, isRiskScoreExist, name, diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_navigate_to_alerts_page_with_filters.test.ts b/x-pack/plugins/security_solution/public/common/hooks/use_navigate_to_alerts_page_with_filters.test.ts index 6a957318f278f..3bfc0c56e81fa 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_navigate_to_alerts_page_with_filters.test.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/use_navigate_to_alerts_page_with_filters.test.ts @@ -32,6 +32,7 @@ describe('useNavigateToAlertsPageWithFilters', () => { expect(mockNavigateTo).toHaveBeenCalledWith({ deepLinkId: SecurityPageName.alerts, path: "?pageFilters=!((exclude:!f,existsSelected:!f,fieldName:'test field',hideActionBar:!f,selectedOptions:!('test value'),title:'test filter'))", + openInNewTab: false, }); }); @@ -63,6 +64,7 @@ describe('useNavigateToAlertsPageWithFilters', () => { expect(mockNavigateTo).toHaveBeenCalledWith({ deepLinkId: SecurityPageName.alerts, path: "?pageFilters=!((exclude:!f,existsSelected:!f,fieldName:'test field 1',hideActionBar:!f,selectedOptions:!('test value 1'),title:'test filter 1'),(exclude:!t,existsSelected:!t,fieldName:'test field 2',hideActionBar:!t,selectedOptions:!('test value 2'),title:'test filter 2'))", + openInNewTab: false, }); }); diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_navigate_to_alerts_page_with_filters.ts b/x-pack/plugins/security_solution/public/common/hooks/use_navigate_to_alerts_page_with_filters.ts index fffa65797b3f8..037bac32d8c92 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_navigate_to_alerts_page_with_filters.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/use_navigate_to_alerts_page_with_filters.ts @@ -16,7 +16,7 @@ import { URL_PARAM_KEY } from './use_url_state'; export const useNavigateToAlertsPageWithFilters = () => { const { navigateTo } = useNavigation(); - return (filterItems: FilterControlConfig | FilterControlConfig[]) => { + return (filterItems: FilterControlConfig | FilterControlConfig[], openInNewTab = false) => { const urlFilterParams = encode( formatPageFilterSearchParam(Array.isArray(filterItems) ? filterItems : [filterItems]) ); @@ -24,6 +24,7 @@ export const useNavigateToAlertsPageWithFilters = () => { navigateTo({ deepLinkId: SecurityPageName.alerts, path: `?${URL_PARAM_KEY.pageFilter}=${urlFilterParams}`, + openInNewTab, }); }; }; diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.tsx index 6e5774ba1756e..107cb83ddd97b 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.tsx @@ -25,6 +25,7 @@ export interface HostDetailsPanelProps extends Record { scopeId: string; hasMisconfigurationFindings?: boolean; hasVulnerabilitiesFindings?: boolean; + hasNonClosedAlerts?: boolean; path?: { tab?: EntityDetailsLeftPanelTab; subTab?: CspInsightLeftPanelSubTab; @@ -43,6 +44,7 @@ export const HostDetailsPanel = ({ path, hasMisconfigurationFindings, hasVulnerabilitiesFindings, + hasNonClosedAlerts, }: HostDetailsPanelProps) => { const [selectedTabId, setSelectedTabId] = useState( path?.tab === EntityDetailsLeftPanelTab.CSP_INSIGHTS @@ -58,11 +60,18 @@ export const HostDetailsPanel = ({ // Determine if the Insights tab should be included const insightsTab = - hasMisconfigurationFindings || hasVulnerabilitiesFindings + hasMisconfigurationFindings || hasVulnerabilitiesFindings || hasNonClosedAlerts ? [getInsightsInputTab({ name, fieldName: 'host.name' })] : []; return [[...riskScoreTab, ...insightsTab], EntityDetailsLeftPanelTab.RISK_INPUTS, () => {}]; - }, [isRiskScoreExist, name, scopeId, hasMisconfigurationFindings, hasVulnerabilitiesFindings]); + }, [ + isRiskScoreExist, + name, + scopeId, + hasMisconfigurationFindings, + hasVulnerabilitiesFindings, + hasNonClosedAlerts, + ]); return ( <> diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/index.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/index.tsx index 83fa75474a1cc..a7e99898606f8 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/host_right/index.tsx @@ -13,6 +13,8 @@ import { buildEntityFlyoutPreviewQuery } from '@kbn/cloud-security-posture-commo import { useMisconfigurationPreview } from '@kbn/cloud-security-posture/src/hooks/use_misconfiguration_preview'; import { useVulnerabilitiesPreview } from '@kbn/cloud-security-posture/src/hooks/use_vulnerabilities_preview'; import { sum } from 'lodash'; +import { DETECTION_RESPONSE_ALERTS_BY_STATUS_ID } from '../../../overview/components/detection_response/alerts_by_status/types'; +import { useAlertsByStatus } from '../../../overview/components/detection_response/alerts_by_status/use_alerts_by_status'; import { useRefetchQueryById } from '../../../entity_analytics/api/hooks/use_refetch_query_by_id'; import { RISK_INPUTS_TAB_QUERY_ID } from '../../../entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs_tab'; import type { Refetch } from '../../../common/types'; @@ -35,6 +37,7 @@ import { useObservedHost } from './hooks/use_observed_host'; import { HostDetailsPanelKey } from '../host_details_left'; import { EntityDetailsLeftPanelTab } from '../shared/components/left_panel/left_panel_header'; import { HostPreviewPanelFooter } from '../host_preview/footer'; +import { useSignalIndex } from '../../../detections/containers/detection_engine/alerts/use_signal_index'; import { EntityEventTypes } from '../../../common/lib/telemetry'; export interface HostPanelProps extends Record { @@ -120,6 +123,21 @@ export const HostPanel = ({ const hasVulnerabilitiesFindings = sum(Object.values(vulnerabilitiesData?.count || {})) > 0; + const { signalIndexName } = useSignalIndex(); + + const entityFilter = useMemo(() => ({ field: 'host.name', value: hostName }), [hostName]); + + const { items: alertsData } = useAlertsByStatus({ + entityFilter, + signalIndexName, + queryId: `${DETECTION_RESPONSE_ALERTS_BY_STATUS_ID}HOST_NAME_RIGHT`, + to, + from, + }); + + const hasNonClosedAlerts = + (alertsData?.acknowledged?.total || 0) + (alertsData?.open?.total || 0) > 0; + useQueryInspector({ deleteQuery, inspect: inspectRiskScore, @@ -144,6 +162,7 @@ export const HostPanel = ({ path: tab ? { tab } : undefined, hasMisconfigurationFindings, hasVulnerabilitiesFindings, + hasNonClosedAlerts, }, }); }, @@ -155,6 +174,7 @@ export const HostPanel = ({ isRiskScoreExist, hasMisconfigurationFindings, hasVulnerabilitiesFindings, + hasNonClosedAlerts, ] ); diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/left_panel/left_panel_header.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/left_panel/left_panel_header.tsx index 08623c941ba67..254985b865840 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/left_panel/left_panel_header.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/left_panel/left_panel_header.tsx @@ -28,6 +28,7 @@ export enum EntityDetailsLeftPanelTab { export enum CspInsightLeftPanelSubTab { MISCONFIGURATIONS = 'misconfigurationTabId', VULNERABILITIES = 'vulnerabilitiesTabId', + ALERTS = 'alertsTabId', } export interface PanelHeaderProps { diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/index.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/index.tsx index 8e6cf3a9ee9d2..87c9e5abc7afd 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/index.tsx @@ -29,6 +29,7 @@ export interface UserDetailsPanelProps extends Record { path?: PanelPath; scopeId: string; hasMisconfigurationFindings?: boolean; + hasNonClosedAlerts?: boolean; } export interface UserDetailsExpandableFlyoutProps extends FlyoutPanelProps { key: 'user_details'; @@ -42,6 +43,7 @@ export const UserDetailsPanel = ({ path, scopeId, hasMisconfigurationFindings, + hasNonClosedAlerts, }: UserDetailsPanelProps) => { const managedUser = useManagedUser(user.name, user.email); const tabs = useTabs( @@ -49,7 +51,8 @@ export const UserDetailsPanel = ({ user.name, isRiskScoreExist, scopeId, - hasMisconfigurationFindings + hasMisconfigurationFindings, + hasNonClosedAlerts ); const { selectedTabId, setSelectedTabId } = useSelectedTab( @@ -57,7 +60,8 @@ export const UserDetailsPanel = ({ user, tabs, path, - hasMisconfigurationFindings + hasMisconfigurationFindings, + hasNonClosedAlerts ); if (managedUser.isLoading) return ; @@ -83,7 +87,8 @@ const useSelectedTab = ( user: UserParam, tabs: LeftPanelTabsType, path: PanelPath | undefined, - hasMisconfigurationFindings?: boolean + hasMisconfigurationFindings?: boolean, + hasNonClosedAlerts?: boolean ) => { const { openLeftPanel } = useExpandableFlyoutApi(); @@ -101,6 +106,7 @@ const useSelectedTab = ( user, isRiskScoreExist, hasMisconfigurationFindings, + hasNonClosedAlerts, path: { tab: tabId, }, diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs.tsx index 6f27b054759f2..0c1cdcaa904a9 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_details_left/tabs.tsx @@ -30,7 +30,8 @@ export const useTabs = ( name: string, isRiskScoreExist: boolean, scopeId: string, - hasMisconfigurationFindings?: boolean + hasMisconfigurationFindings?: boolean, + hasNonClosedAlerts?: boolean ): LeftPanelTabsType => useMemo(() => { const tabs: LeftPanelTabsType = []; @@ -55,12 +56,19 @@ export const useTabs = ( tabs.push(getEntraTab(entraManagedUser)); } - if (hasMisconfigurationFindings) { + if (hasMisconfigurationFindings || hasNonClosedAlerts) { tabs.push(getInsightsInputTab({ name, fieldName: 'user.name' })); } return tabs; - }, [hasMisconfigurationFindings, isRiskScoreExist, managedUser, name, scopeId]); + }, [ + hasMisconfigurationFindings, + hasNonClosedAlerts, + isRiskScoreExist, + managedUser, + name, + scopeId, + ]); const getOktaTab = (oktaManagedUser: ManagedUserHit) => ({ id: EntityDetailsLeftPanelTab.OKTA, diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx index 42c8664b2ac0c..07762ed9aea0c 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx @@ -33,6 +33,9 @@ import { UserDetailsPanelKey } from '../user_details_left'; import { useObservedUser } from './hooks/use_observed_user'; import { EntityDetailsLeftPanelTab } from '../shared/components/left_panel/left_panel_header'; import { UserPreviewPanelFooter } from '../user_preview/footer'; +import { useSignalIndex } from '../../../detections/containers/detection_engine/alerts/use_signal_index'; +import { useAlertsByStatus } from '../../../overview/components/detection_response/alerts_by_status/use_alerts_by_status'; +import { DETECTION_RESPONSE_ALERTS_BY_STATUS_ID } from '../../../overview/components/detection_response/alerts_by_status/types'; import { EntityEventTypes } from '../../../common/lib/telemetry'; export interface UserPanelProps extends Record { @@ -112,6 +115,21 @@ export const UserPanel = ({ const hasMisconfigurationFindings = passedFindings > 0 || failedFindings > 0; + const { signalIndexName } = useSignalIndex(); + + const entityFilter = useMemo(() => ({ field: 'user.name', value: userName }), [userName]); + + const { items: alertsData } = useAlertsByStatus({ + entityFilter, + signalIndexName, + queryId: `${DETECTION_RESPONSE_ALERTS_BY_STATUS_ID}USER_NAME_RIGHT`, + to, + from, + }); + + const hasNonClosedAlerts = + (alertsData?.acknowledged?.total || 0) + (alertsData?.open?.total || 0) > 0; + useQueryInspector({ deleteQuery, inspect, @@ -139,6 +157,7 @@ export const UserPanel = ({ }, path: tab ? { tab } : undefined, hasMisconfigurationFindings, + hasNonClosedAlerts, }, }); }, @@ -150,6 +169,7 @@ export const UserPanel = ({ userName, email, hasMisconfigurationFindings, + hasNonClosedAlerts, ] ); const openPanelFirstTab = useCallback( @@ -191,7 +211,8 @@ export const UserPanel = ({ <> diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_patch.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_patch.test.ts index 49592aff28f95..abf90c3f4dfc4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_patch.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_patch.test.ts @@ -453,4 +453,36 @@ describe('applyRulePatch', () => { }) ).rejects.toThrowError('new_terms_fields: Expected array, received string'); }); + + test('should retain existing required_fields when not present in rule patch body', async () => { + const rulePatch = { + name: 'new name', + } as PatchRuleRequestBody; + const existingRule = { + ...getRulesSchemaMock(), + required_fields: [ + { + name: 'event.action', + type: 'keyword', + ecs: true, + }, + ], + }; + const patchedRule = await applyRulePatch({ + rulePatch, + existingRule, + prebuiltRuleAssetClient, + }); + expect(patchedRule).toEqual( + expect.objectContaining({ + required_fields: [ + { + name: 'event.action', + type: 'keyword', + ecs: true, + }, + ], + }) + ); + }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_patch.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_patch.ts index becc68f3d0075..9f5b167322491 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_patch.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_patch.ts @@ -92,7 +92,9 @@ export const applyRulePatch = async ({ meta: rulePatch.meta ?? existingRule.meta, max_signals: rulePatch.max_signals ?? existingRule.max_signals, related_integrations: rulePatch.related_integrations ?? existingRule.related_integrations, - required_fields: addEcsToRequiredFields(rulePatch.required_fields), + required_fields: rulePatch.required_fields + ? addEcsToRequiredFields(rulePatch.required_fields) + : existingRule.required_fields, risk_score: rulePatch.risk_score ?? existingRule.risk_score, risk_score_mapping: rulePatch.risk_score_mapping ?? existingRule.risk_score_mapping, rule_name_override: rulePatch.rule_name_override ?? existingRule.rule_name_override, diff --git a/x-pack/plugins/session_view/server/routes/alert_status_route.ts b/x-pack/plugins/session_view/server/routes/alert_status_route.ts index e0b95f9705e9d..64192198b5e46 100644 --- a/x-pack/plugins/session_view/server/routes/alert_status_route.ts +++ b/x-pack/plugins/session_view/server/routes/alert_status_route.ts @@ -31,6 +31,11 @@ export const registerAlertStatusRoute = ( .addVersion( { version: '1', + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, validate: { request: { query: schema.object({ diff --git a/x-pack/plugins/session_view/server/routes/alerts_route.ts b/x-pack/plugins/session_view/server/routes/alerts_route.ts index c6b7fd8db7896..c875236989efe 100644 --- a/x-pack/plugins/session_view/server/routes/alerts_route.ts +++ b/x-pack/plugins/session_view/server/routes/alerts_route.ts @@ -36,6 +36,11 @@ export const registerAlertsRoute = ( .addVersion( { version: '1', + security: { + authz: { + requiredPrivileges: ['securitySolution'], + }, + }, validate: { request: { query: schema.object({ diff --git a/x-pack/plugins/session_view/server/routes/get_total_io_bytes_route.ts b/x-pack/plugins/session_view/server/routes/get_total_io_bytes_route.ts index 50f36ac47f5a4..7d54654c89cdc 100644 --- a/x-pack/plugins/session_view/server/routes/get_total_io_bytes_route.ts +++ b/x-pack/plugins/session_view/server/routes/get_total_io_bytes_route.ts @@ -22,6 +22,12 @@ export const registerGetTotalIOBytesRoute = (router: IRouter, logger: Logger) => .addVersion( { version: '1', + security: { + authz: { + enabled: false, + reason: `This route delegates authorization to Elasticsearch and it's not tied to a Kibana privilege.`, + }, + }, validate: { request: { query: schema.object({ diff --git a/x-pack/plugins/session_view/server/routes/io_events_route.ts b/x-pack/plugins/session_view/server/routes/io_events_route.ts index 9810f9da5aa77..3e73517a978c3 100644 --- a/x-pack/plugins/session_view/server/routes/io_events_route.ts +++ b/x-pack/plugins/session_view/server/routes/io_events_route.ts @@ -29,6 +29,12 @@ export const registerIOEventsRoute = (router: IRouter, logger: Logger) => { .addVersion( { version: '1', + security: { + authz: { + enabled: false, + reason: `This route delegates authorization to Elasticsearch and it's not tied to a Kibana privilege.`, + }, + }, validate: { request: { query: schema.object({ diff --git a/x-pack/plugins/session_view/server/routes/process_events_route.ts b/x-pack/plugins/session_view/server/routes/process_events_route.ts index bc6b24fc36bc5..b30b3b6ddcc51 100644 --- a/x-pack/plugins/session_view/server/routes/process_events_route.ts +++ b/x-pack/plugins/session_view/server/routes/process_events_route.ts @@ -43,6 +43,12 @@ export const registerProcessEventsRoute = ( .addVersion( { version: '1', + security: { + authz: { + enabled: false, + reason: `This route delegates authorization to Elasticsearch and it's not tied to a Kibana privilege.`, + }, + }, validate: { request: { query: schema.object({ diff --git a/x-pack/test/api_integration/apis/management/index_management/lib/templates.helpers.ts b/x-pack/test/api_integration/apis/management/index_management/lib/templates.helpers.ts index ea1e9a2d83bb1..a342df2d6287e 100644 --- a/x-pack/test/api_integration/apis/management/index_management/lib/templates.helpers.ts +++ b/x-pack/test/api_integration/apis/management/index_management/lib/templates.helpers.ts @@ -59,6 +59,7 @@ export function templatesHelpers(getService: FtrProviderContext['getService']) { name, indexPatterns, version: 1, + indexMode: 'standard', template: { ...getTemplateMock(isMappingsSourceFieldEnabled) }, _kbnMeta: { isLegacy, diff --git a/x-pack/test/api_integration/apis/management/index_management/templates.ts b/x-pack/test/api_integration/apis/management/index_management/templates.ts index 1fe7e022bfc9a..066df3120be08 100644 --- a/x-pack/test/api_integration/apis/management/index_management/templates.ts +++ b/x-pack/test/api_integration/apis/management/index_management/templates.ts @@ -92,6 +92,7 @@ export default function ({ getService }: FtrProviderContext) { const expectedKeys = [ 'name', 'indexPatterns', + 'indexMode', 'hasSettings', 'hasAliases', 'hasMappings', @@ -115,6 +116,7 @@ export default function ({ getService }: FtrProviderContext) { const expectedLegacyKeys = [ 'name', 'indexPatterns', + 'indexMode', 'hasSettings', 'hasAliases', 'hasMappings', @@ -138,6 +140,7 @@ export default function ({ getService }: FtrProviderContext) { const expectedWithDSLKeys = [ 'name', 'indexPatterns', + 'indexMode', 'lifecycle', 'hasSettings', 'hasAliases', @@ -163,6 +166,7 @@ export default function ({ getService }: FtrProviderContext) { const expectedWithILMKeys = [ 'name', 'indexPatterns', + 'indexMode', 'ilmPolicy', 'hasSettings', 'hasAliases', @@ -190,6 +194,7 @@ export default function ({ getService }: FtrProviderContext) { const expectedKeys = [ 'name', 'indexPatterns', + 'indexMode', 'template', 'composedOf', 'ignoreMissingComponentTemplates', @@ -213,6 +218,7 @@ export default function ({ getService }: FtrProviderContext) { const expectedKeys = [ 'name', 'indexPatterns', + 'indexMode', 'template', 'order', 'version', @@ -375,6 +381,7 @@ export default function ({ getService }: FtrProviderContext) { _kbnMeta: { hasDatastream: false, type: 'default' }, name: templateName, indexPatterns: [getRandomString()], + indexMode: 'standard', template: {}, deprecated: true, allowAutoCreate: 'TRUE', diff --git a/x-pack/test/api_integration/apis/synthetics/get_monitor_project.ts b/x-pack/test/api_integration/apis/synthetics/get_monitor_project.ts index 53089d2bec2d3..dd3fcbef3088d 100644 --- a/x-pack/test/api_integration/apis/synthetics/get_monitor_project.ts +++ b/x-pack/test/api_integration/apis/synthetics/get_monitor_project.ts @@ -95,6 +95,9 @@ export default function ({ getService }: FtrProviderContext) { const firstPageResponse = await supertest .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', project)) + .query({ + per_page: 500, + }) .set('kbn-xsrf', 'true') .send() .expect(200); @@ -185,6 +188,9 @@ export default function ({ getService }: FtrProviderContext) { const firstPageResponse = await supertest .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', project)) + .query({ + per_page: 500, + }) .set('kbn-xsrf', 'true') .send() .expect(200); @@ -279,6 +285,9 @@ export default function ({ getService }: FtrProviderContext) { const firstPageResponse = await supertest .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', project)) + .query({ + per_page: 500, + }) .set('kbn-xsrf', 'true') .send() .expect(200); @@ -372,6 +381,9 @@ export default function ({ getService }: FtrProviderContext) { .expect(200); const firstPageResponse = await supertest .get(SYNTHETICS_API_URLS.SYNTHETICS_MONITORS_PROJECT.replace('{projectName}', project)) + .query({ + per_page: 500, + }) .set('kbn-xsrf', 'true') .send() .expect(200); @@ -481,6 +493,9 @@ export default function ({ getService }: FtrProviderContext) { encodeURI(projectName) ) ) + .query({ + per_page: 500, + }) .set('kbn-xsrf', 'true') .send() .expect(200); diff --git a/x-pack/test/functional/apps/index_management/index_template_wizard.ts b/x-pack/test/functional/apps/index_management/index_template_wizard.ts index 581a0b2761644..d932b96d4f6a1 100644 --- a/x-pack/test/functional/apps/index_management/index_template_wizard.ts +++ b/x-pack/test/functional/apps/index_management/index_template_wizard.ts @@ -67,6 +67,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const stepTitle = await testSubjects.getVisibleText('stepTitle'); expect(stepTitle).to.be('Index settings (optional)'); + // Verify that index mode callout is displayed + const indexModeCalloutText = await testSubjects.getVisibleText('indexModeCallout'); + expect(indexModeCalloutText).to.be( + 'The index.mode setting has been set to Standard within template Logistics. Any changes to index.mode set on this page will be overwritten by the Logistics selection.' + ); + // Click Next button await pageObjects.indexManagement.clickNextButton(); }); diff --git a/x-pack/test/observability_ai_assistant_api_integration/configs/index.ts b/x-pack/test/observability_ai_assistant_api_integration/configs/index.ts index 4a39c31b0a3a7..75f7bb628b4be 100644 --- a/x-pack/test/observability_ai_assistant_api_integration/configs/index.ts +++ b/x-pack/test/observability_ai_assistant_api_integration/configs/index.ts @@ -8,7 +8,6 @@ import { mapValues } from 'lodash'; import path from 'path'; import { createTestConfig, CreateTestConfig } from '../common/config'; -import { SUPPORTED_TRAINED_MODELS } from '../../functional/services/ml/api'; export const observabilityAIAssistantDebugLogger = { name: 'plugins.observabilityAIAssistant', @@ -31,7 +30,6 @@ export const observabilityAIAssistantFtrConfigs = { __dirname, '../../../../test/analytics/plugins/analytics_ftr_helpers' ), - 'xpack.observabilityAIAssistant.modelId': SUPPORTED_TRAINED_MODELS.TINY_ELSER.name, // TODO: Remove }, }, }; diff --git a/x-pack/test/observability_ai_assistant_api_integration/tests/knowledge_base/knowledge_base_setup.spec.ts b/x-pack/test/observability_ai_assistant_api_integration/tests/knowledge_base/knowledge_base_setup.spec.ts index b8cacaaa58351..7903f4b53966a 100644 --- a/x-pack/test/observability_ai_assistant_api_integration/tests/knowledge_base/knowledge_base_setup.spec.ts +++ b/x-pack/test/observability_ai_assistant_api_integration/tests/knowledge_base/knowledge_base_setup.spec.ts @@ -34,7 +34,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { .expect(200); expect(res.body.service_settings.model_id).to.be('pt_tiny_elser'); - expect(res.body.inference_id).to.be('ai_assistant_kb_inference'); + expect(res.body.inference_id).to.be('obs_ai_assistant_kb_inference'); await deleteKnowledgeBaseModel(ml); await deleteInferenceEndpoint({ es }); diff --git a/x-pack/test/observability_ai_assistant_api_integration/tests/knowledge_base/knowledge_base_status.spec.ts b/x-pack/test/observability_ai_assistant_api_integration/tests/knowledge_base/knowledge_base_status.spec.ts index 76ad2d06e344a..8c10a6128d302 100644 --- a/x-pack/test/observability_ai_assistant_api_integration/tests/knowledge_base/knowledge_base_status.spec.ts +++ b/x-pack/test/observability_ai_assistant_api_integration/tests/knowledge_base/knowledge_base_status.spec.ts @@ -77,7 +77,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { expect(res.body.ready).to.be(false); expect(res.body.enabled).to.be(true); expect(res.body.errorMessage).to.include.string( - 'Inference endpoint not found [ai_assistant_kb_inference]' + 'Inference endpoint not found [obs_ai_assistant_kb_inference]' ); }); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules.ts index be8e76f058b14..86dde0735424e 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules.ts @@ -285,6 +285,33 @@ export default ({ getService }: FtrProviderContext) => { ); }); }); + + it('should not change required_fields when not present in patch body', async () => { + await securitySolutionApi.createRule({ + body: getCustomQueryRuleParams({ + rule_id: 'rule-1', + required_fields: [ + { + name: 'event.action', + type: 'keyword', + }, + ], + }), + }); + + // patch a simple rule's name + const { body: patchedRule } = await securitySolutionApi + .patchRule({ body: { rule_id: 'rule-1', name: 'some other name' } }) + .expect(200); + + expect(patchedRule.required_fields).toEqual([ + { + name: 'event.action', + type: 'keyword', + ecs: true, + }, + ]); + }); }); }); }; diff --git a/x-pack/test_serverless/api_integration/services/index_management/svl_templates.helpers.ts b/x-pack/test_serverless/api_integration/services/index_management/svl_templates.helpers.ts index 0f6006fd91470..8c96011936743 100644 --- a/x-pack/test_serverless/api_integration/services/index_management/svl_templates.helpers.ts +++ b/x-pack/test_serverless/api_integration/services/index_management/svl_templates.helpers.ts @@ -56,6 +56,7 @@ export function SvlTemplatesHelpers({ getService }: FtrProviderContext) { const baseTemplate: TemplateDeserialized = { name, indexPatterns, + indexMode: 'standard', version: 1, template: { ...getTemplateMock(isMappingsSourceFieldEnabled) }, _kbnMeta: { diff --git a/x-pack/test_serverless/api_integration/test_suites/common/index_management/index_templates.ts b/x-pack/test_serverless/api_integration/test_suites/common/index_management/index_templates.ts index 1e0dff8bef632..1190a642a3518 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/index_management/index_templates.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/index_management/index_templates.ts @@ -90,6 +90,7 @@ export default function ({ getService }: FtrProviderContext) { const expectedKeys = [ 'name', 'indexPatterns', + 'indexMode', 'hasSettings', 'hasAliases', 'hasMappings', @@ -114,6 +115,7 @@ export default function ({ getService }: FtrProviderContext) { const expectedKeys = [ 'name', 'indexPatterns', + 'indexMode', 'template', '_kbnMeta', 'allowAutoCreate', diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/config.ts b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/config.ts index 14078f228c7c8..01e470d2a7d88 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/config.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/config.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { SUPPORTED_TRAINED_MODELS } from '@kbn/test-suites-xpack/functional/services/ml/api'; import { createTestConfig } from '../../../config.base'; import { ObservabilityAIAssistantServices } from './common/ftr_provider_context'; import { services as inheritedServices } from '../../../services'; @@ -28,7 +27,4 @@ export default createTestConfig({ // include settings from project controller // https://github.com/elastic/project-controller/blob/main/internal/project/observability/config/elasticsearch.yml esServerArgs: ['xpack.ml.dfa.enabled=false'], - kbnServerArgs: [ - `--xpack.observabilityAIAssistant.modelId=${SUPPORTED_TRAINED_MODELS.TINY_ELSER.name}`, // TODO: Remove - ], }); diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base_setup.spec.ts b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base_setup.spec.ts index a792b01b0e2cb..eeef06464c9d6 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base_setup.spec.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/ai_assistant/tests/knowledge_base/knowledge_base_setup.spec.ts @@ -56,7 +56,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { .expect(200); expect(res.body.service_settings.model_id).to.be('pt_tiny_elser'); - expect(res.body.inference_id).to.be('ai_assistant_kb_inference'); + expect(res.body.inference_id).to.be('obs_ai_assistant_kb_inference'); await deleteKnowledgeBaseModel(ml); await deleteInferenceEndpoint({ es }); diff --git a/x-pack/test_serverless/functional/test_suites/search/elasticsearch_start.ts b/x-pack/test_serverless/functional/test_suites/search/elasticsearch_start.ts index 39228137cf7d7..ba370871c07ff 100644 --- a/x-pack/test_serverless/functional/test_suites/search/elasticsearch_start.ts +++ b/x-pack/test_serverless/functional/test_suites/search/elasticsearch_start.ts @@ -26,7 +26,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await esDeleteAllIndices(['search-*', 'test-*']); }; - describe('Elasticsearch Start [Onboarding Empty State]', function () { + // Failing: See https://github.com/elastic/kibana/issues/200020 + describe.skip('Elasticsearch Start [Onboarding Empty State]', function () { describe('developer', function () { before(async () => { await pageObjects.svlCommonPage.loginWithRole('developer'); diff --git a/yarn.lock b/yarn.lock index 2dd177d56de3c..b69c7755172e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1394,10 +1394,10 @@ resolved "https://registry.yarnpkg.com/@bazel/ibazel/-/ibazel-0.16.2.tgz#05dd7f06659759fda30f87b15534f1e42f1201bb" integrity sha512-KgqAWMH0emL6f3xH6nqyTryoBMqlJ627LBIe9PT1PRRQPz2FtHib3FIHJPukp1slzF3hJYZvdiVwgPnHbaSOOA== -"@bazel/runfiles@^5.8.1": - version "5.8.1" - resolved "https://registry.yarnpkg.com/@bazel/runfiles/-/runfiles-5.8.1.tgz#737d5b3dc9739767054820265cfe432a80564c82" - integrity sha512-NDdfpdQ6rZlylgv++iMn5FkObC/QlBQvipinGLSOguTYpRywmieOyJ29XHvUilspwTFSILWpoE9CqMGkHXug1g== +"@bazel/runfiles@^6.3.1": + version "6.3.1" + resolved "https://registry.yarnpkg.com/@bazel/runfiles/-/runfiles-6.3.1.tgz#3f8824b2d82853377799d42354b4df78ab0ace0b" + integrity sha512-1uLNT5NZsUVIGS4syuHwTzZ8HycMPyr6POA3FCE4GbMtc4rhoJk8aZKtNIRthJYfL+iioppi+rTfH3olMPr9nA== "@bazel/typescript@4.6.2": version "4.6.2" @@ -7447,25 +7447,25 @@ dependencies: ncp "^2.0.0" -"@launchdarkly/js-sdk-common@2.11.0": - version "2.11.0" - resolved "https://registry.yarnpkg.com/@launchdarkly/js-sdk-common/-/js-sdk-common-2.11.0.tgz#efc0c94ee4b11d72910c5bcdf9294154a27dbb1c" - integrity sha512-96Jg4QH347w2+rL4Bpykqw28+HHUAW4HapjIkIfM3giELK7BwXUp3BiAVxo2ax78e7A7KqvMzPJUx5r2EGpkMw== +"@launchdarkly/js-sdk-common@2.12.0": + version "2.12.0" + resolved "https://registry.yarnpkg.com/@launchdarkly/js-sdk-common/-/js-sdk-common-2.12.0.tgz#c22eb9fead687260d916a75f693c7d399f085b05" + integrity sha512-HIDxvgo1vksC9hsYy3517sgW0Ql+iW3fgwlq/CEigeBNmaa9/J1Pxo7LrKPzezEA0kaGedmt/DCzVVxVBmxSsQ== -"@launchdarkly/js-server-sdk-common@2.9.0": - version "2.9.0" - resolved "https://registry.yarnpkg.com/@launchdarkly/js-server-sdk-common/-/js-server-sdk-common-2.9.0.tgz#337d13d4bec596f6244b9723f7ec718ee087bfa5" - integrity sha512-hf/qkn+NvCkyoLl6fl+4Q737p4Jg3T+RnRqdkJDMxO+8aAI+vpXuD9bbhkgYA9XFHyQ9puhufRGzX1BX26b7Rg== +"@launchdarkly/js-server-sdk-common@2.9.1": + version "2.9.1" + resolved "https://registry.yarnpkg.com/@launchdarkly/js-server-sdk-common/-/js-server-sdk-common-2.9.1.tgz#a683d682897c20a6967f5454d932663e4da6fc5c" + integrity sha512-BGIjcfel1hURvX4hM4iVruWecWMntRzh1UuPtV0uOYnXLuETp5lfpqBB6KzFoERnEZoCyX6Tmo+tPFVwteIUGA== dependencies: - "@launchdarkly/js-sdk-common" "2.11.0" + "@launchdarkly/js-sdk-common" "2.12.0" semver "7.5.4" -"@launchdarkly/node-server-sdk@^9.7.0": - version "9.7.0" - resolved "https://registry.yarnpkg.com/@launchdarkly/node-server-sdk/-/node-server-sdk-9.7.0.tgz#87223c2d3ab5fc7186065a0974960c94f73573ad" - integrity sha512-ABOsjcjH9pFdyG1m5++lhP+ngxfx4GMcIfgTp0iSPncuh0dMxCCWSx831gbhxR9M+f2x4EnsQ9HEdwnmwktb9g== +"@launchdarkly/node-server-sdk@^9.7.1": + version "9.7.1" + resolved "https://registry.yarnpkg.com/@launchdarkly/node-server-sdk/-/node-server-sdk-9.7.1.tgz#bb228e5f8c4c7ca52579f909e4150b9a749b04a9" + integrity sha512-razZ/ine5hfHiS7ZNfM1r/G7GNDQ+wHV0pRN2A0Yk5spZTmT/ecu67e+aXu/KAfEQyL0V4ofEkXJpeGl7TYJ5Q== dependencies: - "@launchdarkly/js-server-sdk-common" "2.9.0" + "@launchdarkly/js-server-sdk-common" "2.9.1" https-proxy-agent "^5.0.1" launchdarkly-eventsource "2.0.3" @@ -11334,10 +11334,10 @@ resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-2.4.28.tgz#9ce8fa048c1e8c85cb71d7fe4d704e000226036f" integrity sha512-SMA+fUwULwK7sd/ZJicUztiPs8F1yCPwF3O23Z9uQ32ME5Ha0NmDK9+QTsYE4O2tHXChzXomSWWeIhCnoN1LqA== -"@types/selenium-webdriver@^4.1.26": - version "4.1.26" - resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-4.1.26.tgz#09c696a341cf8cfc1641cded11d14813350b6ca9" - integrity sha512-PUgqsyNffal0eAU0bzGlh37MJo558aporAPZoKqBeB/pF7zhKl1S3zqza0GpwFqgoigNxWhEIJzru75eeYco/w== +"@types/selenium-webdriver@^4.1.27": + version "4.1.27" + resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-4.1.27.tgz#e08000d649df6f099b4099432bd2fece9f50ea7b" + integrity sha512-ALqsj8D7Swb6MnBQuAQ58J3KC3yh6fLGtAmpBmnZX8j+0kmP7NaLt56CuzBw2W2bXPrvHFTgn8iekOQFUKXEQA== dependencies: "@types/node" "*" "@types/ws" "*" @@ -11767,10 +11767,10 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== -"@wdio/logger@^9.0.0": - version "9.0.4" - resolved "https://registry.yarnpkg.com/@wdio/logger/-/logger-9.0.4.tgz#63e901b9f0f29fa1ded5af54006fbd4df2354c33" - integrity sha512-b6gcu0PTVb3fgK4kyAH/k5UUWN5FOUdAfhA4PAY/IZvxZTMFYMqnrZb0WRWWWqL6nu9pcrOVtCOdPBvj0cb+Nw== +"@wdio/logger@^9.1.3": + version "9.1.3" + resolved "https://registry.yarnpkg.com/@wdio/logger/-/logger-9.1.3.tgz#b64b3d2ac642498f3c97580e2f0971f13c1e8fbb" + integrity sha512-cumRMK/gE1uedBUw3WmWXOQ7HtB6DR8EyKQioUz2P0IJtRRpglMBdZV7Svr3b++WWawOuzZHMfbTkJQmaVt8Gw== dependencies: chalk "^5.1.2" loglevel "^1.6.0" @@ -12150,10 +12150,10 @@ resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== -"@zip.js/zip.js@^2.7.48": - version "2.7.51" - resolved "https://registry.yarnpkg.com/@zip.js/zip.js/-/zip.js-2.7.51.tgz#a434e285048b951a5788d3d2d59aa68f209e7141" - integrity sha512-RKHaebzZZgQkUuzb49/qweN69e8Np9AUZ9QygydDIrbG1njypSAKwkeqIVeuf2JVGBDyB7Z9HKvzPgYrSlv9gw== +"@zip.js/zip.js@^2.7.53": + version "2.7.53" + resolved "https://registry.yarnpkg.com/@zip.js/zip.js/-/zip.js-2.7.53.tgz#bf88e90d8eed562182c01339643bc405446b0578" + integrity sha512-G6Bl5wN9EXXVaTUIox71vIX5Z454zEBe+akKpV4m1tUboIctT5h7ID3QXCJd/Lfy2rSvmkTmZIucf1jGRR4f5A== a-sync-waterfall@^1.0.0: version "1.0.1" @@ -14218,10 +14218,10 @@ chrome-trace-event@^1.0.2: dependencies: tslib "^1.9.0" -chromedriver@^130.0.1: - version "130.0.1" - resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-130.0.1.tgz#24fd5b2c9c9df4ebfc5d28c94eca8658915fbe15" - integrity sha512-JH+OxDZ7gVv02r9oXwj4mQ8JCtj62g0fCD1LMUUYdB/4mPxn/E2ys+1IzXItoE7vXM9fGVc9R1akvXLqwwuSww== +chromedriver@^130.0.4: + version "130.0.4" + resolved "https://registry.yarnpkg.com/chromedriver/-/chromedriver-130.0.4.tgz#55225ddfec428e306116507651f5a24fdb299bd6" + integrity sha512-lpR+PWXszij1k4Ig3t338Zvll9HtCTiwoLM7n4pCCswALHxzmgwaaIFBh3rt9+5wRk9D07oFblrazrBxwaYYAQ== dependencies: "@testim/chrome-version" "^1.1.4" axios "^1.7.4" @@ -18603,19 +18603,19 @@ gcp-metadata@^6.1.0: gaxios "^6.0.0" json-bigint "^1.0.0" -geckodriver@^4.5.1: - version "4.5.1" - resolved "https://registry.yarnpkg.com/geckodriver/-/geckodriver-4.5.1.tgz#624fc01815c1aa498dd3f717f7bd4c6cca0c57b8" - integrity sha512-lGCRqPMuzbRNDWJOQcUqhNqPvNsIFu6yzXF8J/6K3WCYFd2r5ckbeF7h1cxsnjA7YLSEiWzERCt6/gjZ3tW0ug== +geckodriver@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/geckodriver/-/geckodriver-5.0.0.tgz#88437f3812075988bb05b5e19dc4aaa42d200577" + integrity sha512-vn7TtQ3b9VMJtVXsyWtQQl1fyBVFhQy7UvJF96kPuuJ0or5THH496AD3eUyaDD11+EqCxH9t6V+EP9soZQk4YQ== dependencies: - "@wdio/logger" "^9.0.0" - "@zip.js/zip.js" "^2.7.48" + "@wdio/logger" "^9.1.3" + "@zip.js/zip.js" "^2.7.53" decamelize "^6.0.0" http-proxy-agent "^7.0.2" https-proxy-agent "^7.0.5" node-fetch "^3.3.2" tar-fs "^3.0.6" - which "^4.0.0" + which "^5.0.0" gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: version "1.0.0-beta.2" @@ -28129,12 +28129,12 @@ select-hose@^2.0.0: resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= -selenium-webdriver@^4.25.0: - version "4.25.0" - resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.25.0.tgz#3562b49668817974bb1d13d25a50e8bc0264fcf3" - integrity sha512-zl9IX93caOT8wbcCpZzAkEtYa+hNgJ4C5GUN8uhpzggqRLvsg1asfKi0p1uNZC8buYVvsBZbx8S+9MjVAjs4oA== +selenium-webdriver@^4.26.0: + version "4.26.0" + resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.26.0.tgz#23163cdad20388214a4ad17c1f38262a0857c902" + integrity sha512-nA7jMRIPV17mJmAiTDBWN96Sy0Uxrz5CCLb7bLVV6PpL417SyBMPc2Zo/uoREc2EOHlzHwHwAlFtgmSngSY4WQ== dependencies: - "@bazel/runfiles" "^5.8.1" + "@bazel/runfiles" "^6.3.1" jszip "^3.10.1" tmp "^0.2.3" ws "^8.18.0" @@ -32174,10 +32174,10 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -which@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/which/-/which-4.0.0.tgz#cd60b5e74503a3fbcfbf6cd6b4138a8bae644c1a" - integrity sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg== +which@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/which/-/which-5.0.0.tgz#d93f2d93f79834d4363c7d0c23e00d07c466c8d6" + integrity sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ== dependencies: isexe "^3.1.1"