From a65b688fe27a8c0deb9da4192dcaaf2a4b07c49c Mon Sep 17 00:00:00 2001 From: Pablo Machado Date: Thu, 16 May 2024 11:32:43 +0200 Subject: [PATCH] [SecuritySolution] Revamp entity analytics Open API schemas (#182666) ## Summary * Declare all API types using Open API Schema (OAS). * Generate TS types from schemas * Update the code to use the generated types * Validate requests with `buildRouteValidationWithZod ` * Delete `x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/common.schema.yaml`. There were 2 common files. * Auto format `yaml` files for consistency. * Rename `yaml` files from `{name}_schema.yml` to `{name}.schema.yml`. Otherwise, they don't work with the generator. * Rename `RiskScore` to `EntityRiskScoreRecord` (requested by Maxim) * Update yaml version to '1' --- .../asset_criticality/common.gen.ts | 20 +- .../asset_criticality/common.schema.yaml | 33 +- .../create_asset_criticality.schema.yaml | 2 +- .../delete_asset_criticality.schema.yaml | 2 +- .../get_asset_criticality.schema.yaml | 2 +- .../get_asset_criticality_privileges.gen.ts | 43 ++ ...t_asset_criticality_privileges.schema.yaml | 44 +- .../get_asset_criticality_status.gen.ts | 2 +- .../get_asset_criticality_status.schema.yaml | 2 +- .../asset_criticality/index.ts | 1 + .../upload_asset_criticality_csv.gen.ts | 2 +- .../upload_asset_criticality_csv.schema.yaml | 2 +- .../common/after_keys.schema.test.ts | 52 ++ .../api/entity_analytics/common/common.gen.ts | 130 +++-- .../common/common.schema.yaml | 198 ++++--- .../common/risk_weights.schema.test.ts | 216 ++++++++ .../risk_engine/calculation_route.gen.ts | 90 ++++ ...hema.yml => calculation_route.schema.yaml} | 36 +- .../risk_engine/common.gen.ts | 169 ------ .../risk_engine/common.schema.yaml | 224 -------- .../risk_engine/engine_disable_route.gen.ts | 28 + ...a.yml => engine_disable_route.schema.yaml} | 24 +- .../risk_engine/engine_enable_route.gen.ts | 28 + ...ma.yml => engine_enable_route.schema.yaml} | 24 +- .../risk_engine/engine_init_route.gen.ts | 37 ++ .../risk_engine/engine_init_route.schema.yaml | 80 +++ .../risk_engine/engine_init_route_schema.yml | 46 -- .../risk_engine/engine_settings_route.gen.ts | 2 +- .../engine_settings_route.schema.yaml | 2 +- .../risk_engine/engine_status_route.gen.ts | 32 ++ ...ma.yml => engine_status_route.schema.yaml} | 20 +- .../entity_calculation_route.gen.ts | 6 +- .../entity_calculation_route.schema.yaml | 12 +- .../risk_engine/preview_route.gen.ts | 85 +++ ...e_schema.yml => preview_route.schema.yaml} | 40 +- .../risk_engine/after_keys.test.ts | 59 --- .../risk_engine/after_keys.ts | 21 - .../entity_analytics/risk_engine/index.ts | 1 - .../risk_engine/privileges.test.ts | 2 +- .../risk_engine/privileges.ts | 2 +- .../risk_engine/risk_levels.ts | 33 +- .../risk_score_calculation/request_schema.ts | 31 -- .../risk_score_preview/request_schema.ts | 30 -- .../risk_engine/risk_weights/index.ts | 1 - .../risk_engine/risk_weights/schema.test.ts | 234 --------- .../risk_engine/risk_weights/schema.ts | 57 -- .../entity_analytics/risk_engine/types.ts | 54 +- .../security_solution/risk_score/all/index.ts | 23 +- .../public/entity_analytics/api/api.ts | 35 +- .../use_calculate_entity_risk_score.test.ts | 6 +- .../hooks/use_calculate_entity_risk_score.ts | 5 +- .../hooks/use_disable_risk_engine_mutation.ts | 35 +- .../hooks/use_enable_risk_engine_mutation.ts | 35 +- .../hooks/use_init_risk_engine_mutation.ts | 15 +- .../api/hooks/use_preview_risk_scores.ts | 6 +- .../api/hooks/use_risk_engine_status.ts | 8 +- .../api/hooks/use_risk_score_kpi.tsx | 10 +- .../public/entity_analytics/common/utils.ts | 20 +- .../use_asset_criticality.ts | 2 +- .../entity_analytics_header/index.test.tsx | 14 +- .../entity_analytics_header/index.tsx | 8 +- .../__mocks__/index.ts | 10 +- .../chart_content.test.tsx | 2 +- .../index.test.tsx | 16 +- .../tabs/risk_inputs/risk_inputs.test.tsx | 2 +- .../components/risk_information/index.tsx | 10 +- .../risk_score_donut_chart/index.test.tsx | 10 +- .../use_risk_donut_chart_data.test.ts | 20 +- .../components/risk_score_enable_section.tsx | 23 +- .../components/risk_score_preview_section.tsx | 6 +- .../components/risk_score_preview_table.tsx | 14 +- .../components/risk_summary.test.tsx | 2 +- .../components/severity/common/index.test.tsx | 22 +- .../severity/severity_filter_group.test.tsx | 40 +- .../user_risk_score_table/index.test.tsx | 2 +- .../hooks/use_risk_contributing_alerts.ts | 4 +- .../risk_score_summary.test.ts | 6 +- .../users/components/all_users/index.test.tsx | 4 +- .../left/components/host_details.test.tsx | 2 +- .../left/components/user_details.test.tsx | 2 +- .../host_details_left/index.test.tsx | 2 +- .../flyout/entity_details/mocks/index.ts | 4 +- .../new_user_detail/__mocks__/index.ts | 2 +- .../risk_engine/risk_engine_data_client.ts | 19 +- .../risk_engine_privileges.test.ts | 3 +- .../risk_engine/risk_engine_privileges.ts | 2 +- .../risk_engine/routes/disable.ts | 4 +- .../risk_engine/routes/enable.ts | 4 +- .../risk_engine/routes/init.ts | 18 +- .../risk_engine/routes/privileges.ts | 3 +- .../risk_engine/routes/settings.ts | 8 +- .../risk_engine/routes/status.ts | 23 +- .../risk_engine/schema/risk_score_apis.yml | 485 ------------------ .../calculate_and_persist_risk_scores.mock.ts | 6 +- .../calculate_and_persist_risk_scores.ts | 5 +- .../risk_score/calculate_risk_scores.mock.ts | 22 +- .../risk_score/calculate_risk_scores.ts | 18 +- .../entity_analytics/risk_score/helpers.ts | 15 +- .../risk_score/risk_engine_data_writer.ts | 9 +- .../risk_score/risk_score_service.mock.ts | 10 +- .../risk_score/risk_score_service.ts | 8 +- .../risk_score/risk_weights.ts | 31 +- .../risk_score/routes/calculation.test.ts | 16 +- .../risk_score/routes/calculation.ts | 6 +- .../risk_score/routes/entity_calculation.ts | 7 +- .../risk_score/routes/preview.test.ts | 19 +- .../risk_score/routes/preview.ts | 8 +- .../risk_score/tasks/risk_scoring_task.ts | 2 +- .../server/lib/entity_analytics/types.ts | 134 ++--- .../utils/check_and_format_privileges.ts | 2 +- .../factory/risk_score/all/index.test.ts | 2 +- .../risk_score_calculation.ts | 4 +- .../risk_score_entity_calculation.ts | 4 +- .../risk_score_preview.ts | 6 +- .../entity_analytics/utils/risk_engine.ts | 17 +- 115 files changed, 1542 insertions(+), 2066 deletions(-) create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_privileges.gen.ts create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/common/after_keys.schema.test.ts create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/common/risk_weights.schema.test.ts create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/calculation_route.gen.ts rename x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/{calculation_route_schema.yml => calculation_route.schema.yaml} (69%) delete mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/common.gen.ts delete mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/common.schema.yaml create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_disable_route.gen.ts rename x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/{engine_disable_route_schema.yml => engine_disable_route.schema.yaml} (54%) create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_enable_route.gen.ts rename x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/{engine_enable_route_schema.yml => engine_enable_route.schema.yaml} (54%) create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_init_route.gen.ts create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_init_route.schema.yaml delete mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_init_route_schema.yml create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_status_route.gen.ts rename x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/{engine_status_route_schema.yml => engine_status_route.schema.yaml} (89%) create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/preview_route.gen.ts rename x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/{preview_route_schema.yml => preview_route.schema.yaml} (72%) delete mode 100644 x-pack/plugins/security_solution/common/entity_analytics/risk_engine/after_keys.test.ts delete mode 100644 x-pack/plugins/security_solution/common/entity_analytics/risk_engine/after_keys.ts delete mode 100644 x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_score_calculation/request_schema.ts delete mode 100644 x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_score_preview/request_schema.ts delete mode 100644 x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_weights/schema.test.ts delete mode 100644 x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_weights/schema.ts delete mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/schema/risk_score_apis.yml diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/common.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/common.gen.ts index 141991327fb2..99badae28dc4 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/common.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/common.gen.ts @@ -13,7 +13,7 @@ import { z } from 'zod'; * * info: * title: Asset Criticality Common Schema - * version: 1.0.0 + * version: 1 */ export type IdField = z.infer; @@ -33,13 +33,23 @@ export const AssetCriticalityRecordIdParts = z.object({ id_field: IdField, }); +/** + * The criticality level of the asset. + */ +export type AssetCriticalityLevel = z.infer; +export const AssetCriticalityLevel = z.enum([ + 'low_impact', + 'medium_impact', + 'high_impact', + 'extreme_impact', +]); +export type AssetCriticalityLevelEnum = typeof AssetCriticalityLevel.enum; +export const AssetCriticalityLevelEnum = AssetCriticalityLevel.enum; + export type CreateAssetCriticalityRecord = z.infer; export const CreateAssetCriticalityRecord = AssetCriticalityRecordIdParts.merge( z.object({ - /** - * The criticality level of the asset. - */ - criticality_level: z.enum(['low_impact', 'medium_impact', 'high_impact', 'extreme_impact']), + criticality_level: AssetCriticalityLevel, /** * If 'wait_for' the request will wait for the index refresh. */ diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/common.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/common.schema.yaml index 3d3e82524109..294cba2dd89e 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/common.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/common.schema.yaml @@ -2,8 +2,8 @@ openapi: 3.0.0 info: title: Asset Criticality Common Schema description: Common schema for asset criticality - version: 1.0.0 -paths: { } + version: '1' +paths: {} components: parameters: id_value: @@ -41,19 +41,26 @@ components: required: - id_value - id_field + AssetCriticalityLevel: + type: string + enum: + - low_impact + - medium_impact + - high_impact + - extreme_impact + description: The criticality level of the asset. + CreateAssetCriticalityRecord: allOf: - $ref: '#/components/schemas/AssetCriticalityRecordIdParts' - type: object properties: criticality_level: - type: string - enum: [low_impact, medium_impact, high_impact, extreme_impact] - description: The criticality level of the asset. + $ref: '#/components/schemas/AssetCriticalityLevel' refresh: - type: string - enum: [wait_for] - description: If 'wait_for' the request will wait for the index refresh. + type: string + enum: [wait_for] + description: If 'wait_for' the request will wait for the index refresh. required: - criticality_level DeleteAssetCriticalityRecord: @@ -62,18 +69,18 @@ components: - type: object properties: refresh: - type: string - enum: [wait_for] - description: If 'wait_for' the request will wait for the index refresh. + type: string + enum: [wait_for] + description: If 'wait_for' the request will wait for the index refresh. AssetCriticalityRecord: allOf: - $ref: '#/components/schemas/CreateAssetCriticalityRecord' - type: object properties: - "@timestamp": + '@timestamp': type: string format: 'date-time' example: '2017-07-21T17:32:28Z' description: The time the record was created or updated. required: - - "@timestamp" + - '@timestamp' diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/create_asset_criticality.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/create_asset_criticality.schema.yaml index cc8c980809f9..46a0d3132dbe 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/create_asset_criticality.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/create_asset_criticality.schema.yaml @@ -1,6 +1,6 @@ openapi: 3.0.0 info: - version: 1.0.0 + version: '1' title: Asset Criticality Create Record Schema servers: - url: 'http://{kibana_host}:{port}' diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/delete_asset_criticality.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/delete_asset_criticality.schema.yaml index cada6a62fcaa..3c00d590bda9 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/delete_asset_criticality.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/delete_asset_criticality.schema.yaml @@ -1,6 +1,6 @@ openapi: 3.0.0 info: - version: 1.0.0 + version: '1' title: Asset Criticality Delete Record Schema servers: - url: 'http://{kibana_host}:{port}' diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality.schema.yaml index 777666daccb2..1bec2054a65d 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality.schema.yaml @@ -1,6 +1,6 @@ openapi: 3.0.0 info: - version: 1.0.0 + version: '1' title: Asset Criticality Get Record Schema servers: - url: 'http://{kibana_host}:{port}' diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_privileges.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_privileges.gen.ts new file mode 100644 index 000000000000..851fafd4330b --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_privileges.gen.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { z } from 'zod'; + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Get Asset Criticality Privileges Schema + * version: 1 + */ + +export type EntityAnalyticsPrivileges = z.infer; +export const EntityAnalyticsPrivileges = z.object({ + has_all_required: z.boolean(), + has_read_permissions: z.boolean().optional(), + has_write_permissions: z.boolean().optional(), + privileges: z.object({ + elasticsearch: z.object({ + cluster: z + .object({ + manage_index_templates: z.boolean().optional(), + manage_transform: z.boolean().optional(), + }) + .optional(), + index: z + .object({}) + .catchall( + z.object({ + read: z.boolean().optional(), + write: z.boolean().optional(), + }) + ) + .optional(), + }), + }), +}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_privileges.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_privileges.schema.yaml index 6f1734262c66..bd6b2a300ccc 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_privileges.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_privileges.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: title: Get Asset Criticality Privileges Schema - version: 1.0.0 + version: '1' servers: - url: 'http://{kibana_host}:{port}' variables: @@ -20,11 +20,49 @@ paths: content: application/json: schema: - $ref: '../common/common.schema.yaml#/components/schemas/EntityAnalyticsPrivileges' + $ref: '#/components/schemas/EntityAnalyticsPrivileges' example: elasticsearch: index: - ".asset-criticality.asset-criticality-*": + '.asset-criticality.asset-criticality-*': read: true write: false has_all_required: false +components: + schemas: + EntityAnalyticsPrivileges: + type: object + properties: + has_all_required: + type: boolean + has_read_permissions: + type: boolean + has_write_permissions: + type: boolean + privileges: + type: object + properties: + elasticsearch: + type: object + properties: + cluster: + type: object + properties: + manage_index_templates: + type: boolean + manage_transform: + type: boolean + index: + type: object + additionalProperties: + type: object + properties: + read: + type: boolean + write: + type: boolean + required: + - elasticsearch + required: + - has_all_required + - privileges diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_status.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_status.gen.ts index fdace6d552b6..5d7ce2c1424b 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_status.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_status.gen.ts @@ -13,7 +13,7 @@ import { z } from 'zod'; * * info: * title: Asset Criticality Status Schema - * version: 1.0.0 + * version: 1 */ export type AssetCriticalityStatusResponse = z.infer; diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_status.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_status.schema.yaml index a62450cbcff4..312368ea3e4f 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_status.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_status.schema.yaml @@ -1,6 +1,6 @@ openapi: 3.0.0 info: - version: 1.0.0 + version: '1' title: Asset Criticality Status Schema servers: - url: 'http://{kibana_host}:{port}' diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/index.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/index.ts index 2bfe5dc73938..cc79a94a41fc 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/index.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/index.ts @@ -8,3 +8,4 @@ export * from './common.gen'; export * from './get_asset_criticality_status.gen'; export * from './upload_asset_criticality_csv.gen'; +export * from './get_asset_criticality_privileges.gen'; diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/upload_asset_criticality_csv.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/upload_asset_criticality_csv.gen.ts index 67bd49bf7b64..59d50d8a056c 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/upload_asset_criticality_csv.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/upload_asset_criticality_csv.gen.ts @@ -13,7 +13,7 @@ import { z } from 'zod'; * * info: * title: Asset Criticality Create Record Schema - * version: 1.0.0 + * version: 1 */ export type ErrorItem = z.infer; diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/upload_asset_criticality_csv.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/upload_asset_criticality_csv.schema.yaml index 55689eef3938..fcaaa8a36b20 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/upload_asset_criticality_csv.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/upload_asset_criticality_csv.schema.yaml @@ -1,6 +1,6 @@ openapi: 3.0.0 info: - version: 1.0.0 + version: '1' title: Asset Criticality Create Record Schema servers: - url: 'http://{kibana_host}:{port}' diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/common/after_keys.schema.test.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/common/after_keys.schema.test.ts new file mode 100644 index 000000000000..537d28ed6668 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/common/after_keys.schema.test.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { AfterKeys } from '.'; +import type { SafeParseSuccess } from 'zod'; + +describe('after_keys schema', () => { + it('allows an empty object', () => { + const payload = {}; + const decoded = AfterKeys.safeParse(payload) as SafeParseSuccess; + + expect(decoded.success).toBeTruthy(); + expect(decoded.data).toEqual(payload); + }); + + it('allows a valid host key', () => { + const payload = { host: { 'host.name': 'hello' } }; + const decoded = AfterKeys.safeParse(payload) as SafeParseSuccess; + + expect(decoded.success).toBeTruthy(); + expect(decoded.data).toEqual(payload); + }); + + it('allows a valid user key', () => { + const payload = { user: { 'user.name': 'hello' } }; + const decoded = AfterKeys.safeParse(payload) as SafeParseSuccess; + + expect(decoded.success).toBeTruthy(); + expect(decoded.data).toEqual(payload); + }); + + it('allows both valid host and user keys', () => { + const payload = { user: { 'user.name': 'hello' }, host: { 'host.name': 'hello' } }; + const decoded = AfterKeys.safeParse(payload) as SafeParseSuccess; + + expect(decoded.success).toBeTruthy(); + expect(decoded.data).toEqual(payload); + }); + + it('removes an unknown identifier key if used', () => { + const payload = { bad: 'key' }; + + const decoded = AfterKeys.safeParse(payload) as SafeParseSuccess; + + expect(decoded.success).toBeTruthy(); + expect(decoded.data).toEqual({}); + }); +}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.gen.ts index 2c58f0461967..b03753424804 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.gen.ts @@ -13,39 +13,18 @@ import { z } from 'zod'; * * info: * title: Entity Analytics Common Schema - * version: 1.0.0 + * version: 1 */ -export type EntityAnalyticsPrivileges = z.infer; -export const EntityAnalyticsPrivileges = z.object({ - has_all_required: z.boolean(), - has_read_permissions: z.boolean().optional(), - has_write_permissions: z.boolean().optional(), - privileges: z.object({ - elasticsearch: z.object({ - cluster: z - .object({ - manage_index_templates: z.boolean().optional(), - manage_transform: z.boolean().optional(), - }) - .optional(), - index: z - .object({}) - .catchall( - z.object({ - read: z.boolean().optional(), - write: z.boolean().optional(), - }) - ) - .optional(), - }), - }), -}); +import { AssetCriticalityLevel } from '../asset_criticality/common.gen'; + +export type EntityAfterKey = z.infer; +export const EntityAfterKey = z.object({}).catchall(z.string()); export type AfterKeys = z.infer; export const AfterKeys = z.object({ - host: z.object({}).catchall(z.string()).optional(), - user: z.object({}).catchall(z.string()).optional(), + host: EntityAfterKey.optional(), + user: EntityAfterKey.optional(), }); /** @@ -58,7 +37,7 @@ export const DataViewId = z.string(); * An elasticsearch DSL filter object. Used to filter the risk inputs involved, which implicitly filters the risk scores themselves. */ export type Filter = z.infer; -export const Filter = z.object({}); +export const Filter = z.object({}).catchall(z.unknown()); /** * Specifies how many scores will be involved in a given calculation. Note that this value is per `identifier_type`, i.e. a value of 10 will calculate 10 host scores and 10 user scores, if available. To avoid missed data, keep this value consistent while paginating through scores. @@ -91,19 +70,19 @@ export const RiskScoreInput = z.object({ /** * The unique identifier (`_id`) of the original source document */ - id: z.string().optional(), + id: z.string(), /** * The unique index (`_index`) of the original source document */ - index: z.string().optional(), + index: z.string(), /** * The risk category of the risk input document. */ - category: z.string().optional(), + category: z.string(), /** * A human-readable description of the risk input document. */ - description: z.string().optional(), + description: z.string(), /** * The weighted risk score of the risk input document. */ @@ -112,10 +91,19 @@ export const RiskScoreInput = z.object({ * The @timestamp of the risk input document. */ timestamp: z.string().optional(), + contribution_score: z.number().optional(), }); -export type RiskScore = z.infer; -export const RiskScore = z.object({ +export type RiskScoreCategories = z.infer; +export const RiskScoreCategories = z.literal('category_1'); + +export type EntityRiskLevels = z.infer; +export const EntityRiskLevels = z.enum(['Unknown', 'Low', 'Moderate', 'High', 'Critical']); +export type EntityRiskLevelsEnum = typeof EntityRiskLevels.enum; +export const EntityRiskLevelsEnum = EntityRiskLevels.enum; + +export type EntityRiskScoreRecord = z.infer; +export const EntityRiskScoreRecord = z.object({ /** * The time at which the risk score was calculated. */ @@ -131,7 +119,7 @@ export const RiskScore = z.object({ /** * Lexical description of the entity's risk. */ - calculated_level: z.string(), + calculated_level: EntityRiskLevels, /** * The raw numeric value of the given entity's risk score. */ @@ -152,18 +140,64 @@ export const RiskScore = z.object({ * A list of the highest-risk documents contributing to this risk score. Useful for investigative purposes. */ inputs: z.array(RiskScoreInput), + category_2_score: z.number().optional(), + category_2_count: z.number().optional(), + notes: z.array(z.string()), + criticality_modifier: z.number().optional(), + criticality_level: AssetCriticalityLevel.optional(), +}); + +export type RiskScoreEntityIdentifierWeights = z.infer; +export const RiskScoreEntityIdentifierWeights = z.number().min(0).max(1); + +export type RiskScoreWeightGlobalShared = z.infer; +export const RiskScoreWeightGlobalShared = z.object({ + type: z.literal('global_identifier'), }); +export type RiskScoreWeightGlobal = z.infer; +export const RiskScoreWeightGlobal = z.union([ + RiskScoreWeightGlobalShared.merge( + z.object({ + host: RiskScoreEntityIdentifierWeights, + user: RiskScoreEntityIdentifierWeights.optional(), + }) + ), + RiskScoreWeightGlobalShared.merge( + z.object({ + host: RiskScoreEntityIdentifierWeights.optional(), + user: RiskScoreEntityIdentifierWeights, + }) + ), +]); + +export type RiskScoreWeightCategoryShared = z.infer; +export const RiskScoreWeightCategoryShared = z.object({ + type: z.literal('risk_category'), + value: RiskScoreCategories, +}); + +export type RiskScoreWeightCategory = z.infer; +export const RiskScoreWeightCategory = z.union([ + RiskScoreWeightCategoryShared.merge( + z.object({ + host: RiskScoreEntityIdentifierWeights, + user: RiskScoreEntityIdentifierWeights.optional(), + }) + ), + RiskScoreWeightCategoryShared.merge( + z.object({ + host: RiskScoreEntityIdentifierWeights.optional(), + user: RiskScoreEntityIdentifierWeights, + }) + ), +]); + /** * Configuration used to tune risk scoring. Weights can be used to change the score contribution of risk inputs for hosts and users at both a global level and also for Risk Input categories (e.g. 'category_1'). */ export type RiskScoreWeight = z.infer; -export const RiskScoreWeight = z.object({ - type: z.string(), - value: z.string().optional(), - host: z.number().min(0).max(1).optional(), - user: z.number().min(0).max(1).optional(), -}); +export const RiskScoreWeight = z.union([RiskScoreWeightGlobal, RiskScoreWeightCategory]); /** * A list of weights to be applied to the scoring calculation. @@ -171,9 +205,11 @@ export const RiskScoreWeight = z.object({ export type RiskScoreWeights = z.infer; export const RiskScoreWeights = z.array(RiskScoreWeight); -export type RiskEngineInitStep = z.infer; -export const RiskEngineInitStep = z.object({ - type: z.string(), - success: z.boolean(), - error: z.string().optional(), +/** + * Task manager is unavailable + */ +export type TaskManagerUnavailableResponse = z.infer; +export const TaskManagerUnavailableResponse = z.object({ + status_code: z.number().int().min(400), + message: z.string(), }); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.schema.yaml index 9285503c322f..7b6634876d8a 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.schema.yaml @@ -2,57 +2,22 @@ openapi: 3.0.0 info: title: Entity Analytics Common Schema description: Common schema for Entity Analytics - version: 1.0.0 + version: '1' paths: {} components: schemas: - EntityAnalyticsPrivileges: + EntityAfterKey: type: object - properties: - has_all_required: - type: boolean - has_read_permissions: - type: boolean - has_write_permissions: - type: boolean - privileges: - type: object - properties: - elasticsearch: - type: object - properties: - cluster: - type: object - properties: - manage_index_templates: - type: boolean - manage_transform: - type: boolean - index: - type: object - additionalProperties: - type: object - properties: - read: - type: boolean - write: - type: boolean - required: - - elasticsearch - required: - - has_all_required - - privileges + additionalProperties: + type: string + AfterKeys: type: object properties: host: - type: object - additionalProperties: - type: string + $ref: '#/components/schemas/EntityAfterKey' user: - type: object - additionalProperties: - type: string + $ref: '#/components/schemas/EntityAfterKey' example: host: 'host.name': 'example.host' @@ -67,6 +32,7 @@ components: Filter: description: An elasticsearch DSL filter object. Used to filter the risk inputs involved, which implicitly filters the risk scores themselves. type: object + additionalProperties: true PageSize: description: Specifies how many scores will be involved in a given calculation. Note that this value is per `identifier_type`, i.e. a value of 10 will calculate 10 host scores and 10 user scores, if available. To avoid missed data, keep this value consistent while paginating through scores. @@ -98,6 +64,11 @@ components: RiskScoreInput: description: A generic representation of a document contributing to a Risk Score. type: object + required: + - id + - index + - description + - category properties: id: type: string @@ -125,8 +96,25 @@ components: type: string example: '2017-07-21T17:32:28Z' description: The @timestamp of the risk input document. + contribution_score: + type: number + format: double + + RiskScoreCategories: + type: string + enum: + - category_1 + + EntityRiskLevels: + type: string + enum: + - 'Unknown' + - 'Low' + - 'Moderate' + - 'High' + - 'Critical' - RiskScore: + EntityRiskScoreRecord: type: object required: - '@timestamp' @@ -138,6 +126,7 @@ components: - category_1_score - category_1_count - inputs + - notes properties: '@timestamp': type: string @@ -153,7 +142,7 @@ components: example: 'example.host' description: The identifier value defining this risk score. Coupled with `id_field`, uniquely identifies the entity being scored. calculated_level: - type: string + $ref: '#/components/schemas/EntityRiskLevels' example: 'Critical' description: Lexical description of the entity's risk. calculated_score: @@ -179,27 +168,106 @@ components: description: A list of the highest-risk documents contributing to this risk score. Useful for investigative purposes. items: $ref: '#/components/schemas/RiskScoreInput' + category_2_score: + type: number + format: double + category_2_count: + type: number + format: integer + notes: + type: array + items: + type: string + criticality_modifier: + type: number + format: double + criticality_level: + $ref: '../asset_criticality/common.schema.yaml#/components/schemas/AssetCriticalityLevel' - RiskScoreWeight: - description: "Configuration used to tune risk scoring. Weights can be used to change the score contribution of risk inputs for hosts and users at both a global level and also for Risk Input categories (e.g. 'category_1')." + RiskScoreEntityIdentifierWeights: + type: number + format: double + minimum: 0 + maximum: 1 + + RiskScoreWeightGlobalShared: + x-inline: true type: object required: - type properties: type: type: string - value: + enum: + - global_identifier + + RiskScoreWeightGlobal: + oneOf: + - allOf: + - $ref: '#/components/schemas/RiskScoreWeightGlobalShared' + - type: object + required: + - host + properties: + host: + $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' + user: + $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' + + - allOf: + - $ref: '#/components/schemas/RiskScoreWeightGlobalShared' + - type: object + required: + - user + properties: + host: + $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' + user: + $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' + + RiskScoreWeightCategoryShared: + x-inline: true + type: object + required: + - type + - value + properties: + type: type: string - host: - type: number - format: double - minimum: 0 - maximum: 1 - user: - type: number - format: double - minimum: 0 - maximum: 1 + enum: + - risk_category + value: + $ref: '#/components/schemas/RiskScoreCategories' + + RiskScoreWeightCategory: + oneOf: + - allOf: + - $ref: '#/components/schemas/RiskScoreWeightCategoryShared' + - type: object + required: + - host + properties: + host: + $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' + user: + $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' + + - allOf: + - $ref: '#/components/schemas/RiskScoreWeightCategoryShared' + - type: object + required: + - user + properties: + host: + $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' + user: + $ref: '#/components/schemas/RiskScoreEntityIdentifierWeights' + + RiskScoreWeight: + description: "Configuration used to tune risk scoring. Weights can be used to change the score contribution of risk inputs for hosts and users at both a global level and also for Risk Input categories (e.g. 'category_1')." + oneOf: + - $ref: '#/components/schemas/RiskScoreWeightGlobal' + - $ref: '#/components/schemas/RiskScoreWeightCategory' example: type: 'risk_category' value: 'category_1' @@ -220,15 +288,15 @@ components: host: 0.5 user: 0.1 - RiskEngineInitStep: + TaskManagerUnavailableResponse: + description: Task manager is unavailable type: object required: - - type - - success + - status_code + - message properties: - type: - type: string - success: - type: boolean - error: + status_code: + type: integer + minimum: 400 + message: type: string diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/common/risk_weights.schema.test.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/common/risk_weights.schema.test.ts new file mode 100644 index 000000000000..59b0859300f8 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/common/risk_weights.schema.test.ts @@ -0,0 +1,216 @@ +/* + * 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 { RiskScoreWeight } from '.'; +import type { SafeParseError, SafeParseSuccess } from 'zod'; +import { stringifyZodError } from '@kbn/zod-helpers'; +import { RiskCategories, RiskWeightTypes } from '../../../entity_analytics/risk_engine'; + +describe('risk weight schema', () => { + let type: string; + + describe('allowed types', () => { + it('allows the global weight type', () => { + const payload = { + type: RiskWeightTypes.global, + host: 0.1, + }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; + + expect(decoded.success).toBeTruthy(); + expect(decoded.data).toEqual(payload); + }); + + it('allows the risk category weight type', () => { + const payload = { + type: RiskWeightTypes.global, + host: 0.1, + }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; + + expect(decoded.success).toBeTruthy(); + expect(decoded.data).toEqual(payload); + }); + + it('rejects an unknown weight type', () => { + const payload = { + type: 'unknown', + host: 0.1, + }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseError; + + expect(decoded.success).toBeFalsy(); + expect(decoded.error.errors.length).toBeGreaterThan(0); + }); + }); + + describe('conditional fields', () => { + describe('global weights', () => { + beforeEach(() => { + type = RiskWeightTypes.global; + }); + + it('rejects if neither host nor user weight are specified', () => { + const payload = { type }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseError; + + expect(decoded.success).toBeFalsy(); + expect(stringifyZodError(decoded.error)).toEqual( + 'host: Required, user: Required, type: Invalid literal value, expected "risk_category", value: Invalid literal value, expected "category_1", host: Required, and 3 more' + ); + }); + + it('allows a single host weight', () => { + const payload = { type, host: 0.1 }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; + + expect(decoded.success).toBeTruthy(); + expect(decoded.data).toEqual(payload); + }); + + it('allows a single user weight', () => { + const payload = { type, user: 0.1 }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; + + expect(decoded.success).toBeTruthy(); + expect(decoded.data).toEqual(payload); + }); + + it('allows both a host and user weight', () => { + const payload = { type, host: 0.1, user: 0.5 }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; + + expect(decoded.success).toBeTruthy(); + expect(decoded.data).toEqual({ type, host: 0.1, user: 0.5 }); + }); + + it('rejects a weight outside of 0-1', () => { + const payload = { type, user: 55 }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseError; + + expect(decoded.success).toBeFalsy(); + expect(stringifyZodError(decoded.error)).toContain( + `user: Number must be less than or equal to 1` + ); + }); + + it('removes extra keys if specified', () => { + const payload = { + type, + host: 0.1, + value: 'superfluous', + extra: 'even more', + }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; + + expect(decoded.success).toBeTruthy(); + expect(decoded.data).toEqual({ type, host: 0.1 }); + }); + }); + + describe('risk category weights', () => { + beforeEach(() => { + type = RiskWeightTypes.riskCategory; + }); + + it('requires a value', () => { + const payload = { type, user: 0.1 }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseError; + + expect(decoded.success).toBeFalsy(); + expect(stringifyZodError(decoded.error)).toEqual( + 'type: Invalid literal value, expected "global_identifier", host: Required, type: Invalid literal value, expected "global_identifier", value: Invalid literal value, expected "category_1", host: Required, and 1 more' + ); + }); + + it('rejects if neither host nor user weight are specified', () => { + const payload = { type, value: RiskCategories.category_1 }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseError; + + expect(decoded.success).toBeFalsy(); + expect(stringifyZodError(decoded.error)).toEqual( + 'type: Invalid literal value, expected "global_identifier", host: Required, type: Invalid literal value, expected "global_identifier", user: Required, host: Required, and 1 more' + ); + }); + + it('allows a single host weight', () => { + const payload = { type, value: RiskCategories.category_1, host: 0.1 }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; + + expect(decoded.success).toBeTruthy(); + expect(decoded.data).toEqual(payload); + }); + + it('allows a single user weight', () => { + const payload = { type, value: RiskCategories.category_1, user: 0.1 }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; + + expect(decoded.success).toBeTruthy(); + expect(decoded.data).toEqual(payload); + }); + + it('allows both a host and user weight', () => { + const payload = { type, value: RiskCategories.category_1, user: 0.1, host: 0.5 }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; + + expect(decoded.success).toBeTruthy(); + expect(decoded.data).toEqual(payload); + }); + + it('rejects a weight outside of 0-1', () => { + const payload = { type, value: RiskCategories.category_1, host: -5 }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseError; + + expect(decoded.success).toBeFalsy(); + expect(stringifyZodError(decoded.error)).toContain( + `host: Number must be greater than or equal to 0` + ); + }); + + it('removes extra keys if specified', () => { + const payload = { + type, + value: RiskCategories.category_1, + host: 0.1, + extra: 'even more', + }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; + + expect(decoded.success).toBeTruthy(); + expect(decoded.data).toEqual({ type, value: RiskCategories.category_1, host: 0.1 }); + }); + + describe('allowed category values', () => { + it('allows the alerts type for a category', () => { + const payload = { + type, + value: RiskCategories.category_1, + host: 0.1, + }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseSuccess; + + expect(decoded.success).toBeTruthy(); + expect(decoded.data).toEqual(payload); + }); + + it('rejects an unknown category value', () => { + const payload = { + type, + value: 'unknown', + host: 0.1, + }; + const decoded = RiskScoreWeight.safeParse(payload) as SafeParseError; + + expect(decoded.success).toBeFalsy(); + expect(stringifyZodError(decoded.error)).toContain( + 'type: Invalid literal value, expected "global_identifier", type: Invalid literal value, expected "global_identifier", user: Required, value: Invalid literal value, expected "category_1", value: Invalid literal value, expected "category_1", and 1 more' + ); + }); + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/calculation_route.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/calculation_route.gen.ts new file mode 100644 index 000000000000..892f0f2228bd --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/calculation_route.gen.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { z } from 'zod'; + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Risk Scoring API + * version: 1 + */ + +import { + AfterKeys, + DataViewId, + Filter, + PageSize, + IdentifierType, + DateRange, + RiskScoreWeights, + EntityRiskScoreRecord, +} from '../common/common.gen'; + +export type RiskScoresCalculationRequest = z.infer; +export const RiskScoresCalculationRequest = z.object({ + /** + * Used to calculate a specific "page" of risk scores. If unspecified, the first "page" of scores is returned. See also the `after_keys` key in a risk scores response. + */ + after_keys: AfterKeys.optional(), + /** + * The identifier of the Kibana data view to be used when generating risk scores. If a data view is not found, the provided ID will be used as the query's index pattern instead. + */ + data_view_id: DataViewId, + /** + * If set to `true`, the internal ES requests/responses will be logged in Kibana. + */ + debug: z.boolean().optional(), + /** + * An elasticsearch DSL filter object. Used to filter the data being scored, which implicitly filters the risk scores calculated. + */ + filter: Filter.optional(), + page_size: PageSize.optional(), + /** + * Used to restrict the type of risk scores calculated. + */ + identifier_type: IdentifierType, + /** + * Defines the time period over which scores will be evaluated. If unspecified, a range of `[now, now-30d]` will be used. + */ + range: DateRange, + weights: RiskScoreWeights.optional(), +}); + +export type RiskScoresCalculationResponse = z.infer; +export const RiskScoresCalculationResponse = z.object({ + /** + * Used to obtain the next "page" of risk scores. See also the `after_keys` key in a risk scores request. If this key is empty, the calculation is complete. + */ + after_keys: AfterKeys, + /** + * A list of errors encountered during the calculation. + */ + errors: z.array(z.string()), + /** + * The number of risk scores persisted to elasticsearch. + */ + scores_written: z.number(), + scores: z + .object({ + /** + * A list of host risk scores + */ + host: z.array(EntityRiskScoreRecord).optional(), + /** + * A list of user risk scores + */ + user: z.array(EntityRiskScoreRecord).optional(), + /** + * If 'wait_for' the request will wait for the index refresh. + */ + refresh: z.literal('wait_for').optional(), + }) + .optional(), +}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/calculation_route_schema.yml b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/calculation_route.schema.yaml similarity index 69% rename from x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/calculation_route_schema.yml rename to x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/calculation_route.schema.yaml index c851509c4f64..2b1a6d39b884 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/calculation_route_schema.yml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/calculation_route.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: - version: 1.0.0 + version: '1' title: Risk Scoring API description: These APIs allow the consumer to manage Entity Risk Scores within Entity Analytics. @@ -46,28 +46,27 @@ components: properties: after_keys: description: Used to calculate a specific "page" of risk scores. If unspecified, the first "page" of scores is returned. See also the `after_keys` key in a risk scores response. - allOf: - - $ref: 'common.yml#/components/schemas/AfterKeys' + $ref: '../common/common.schema.yaml#/components/schemas/AfterKeys' data_view_id: - $ref: 'common.yml#/components/schemas/DataViewId' + $ref: '../common/common.schema.yaml#/components/schemas/DataViewId' description: The identifier of the Kibana data view to be used when generating risk scores. If a data view is not found, the provided ID will be used as the query's index pattern instead. debug: description: If set to `true`, the internal ES requests/responses will be logged in Kibana. type: boolean filter: - $ref: 'common.yml#/components/schemas/Filter' + $ref: '../common/common.schema.yaml#/components/schemas/Filter' description: An elasticsearch DSL filter object. Used to filter the data being scored, which implicitly filters the risk scores calculated. page_size: - $ref: 'common.yml#/components/schemas/PageSize' + $ref: '../common/common.schema.yaml#/components/schemas/PageSize' identifier_type: description: Used to restrict the type of risk scores calculated. allOf: - - $ref: 'common.yml#/components/schemas/IdentifierType' + - $ref: '../common/common.schema.yaml#/components/schemas/IdentifierType' range: - $ref: 'common.yml#/components/schemas/DateRange' + $ref: '../common/common.schema.yaml#/components/schemas/DateRange' description: Defines the time period over which scores will be evaluated. If unspecified, a range of `[now, now-30d]` will be used. weights: - $ref: 'common.yml#/components/schemas/RiskScoreWeights' + $ref: '../common/common.schema.yaml#/components/schemas/RiskScoreWeights' RiskScoresCalculationResponse: type: object @@ -79,7 +78,7 @@ components: after_keys: description: Used to obtain the next "page" of risk scores. See also the `after_keys` key in a risk scores request. If this key is empty, the calculation is complete. allOf: - - $ref: 'common.yml#/components/schemas/AfterKeys' + - $ref: '../common/common.schema.yaml#/components/schemas/AfterKeys' errors: type: array description: A list of errors encountered during the calculation. @@ -89,3 +88,20 @@ components: type: number format: integer description: The number of risk scores persisted to elasticsearch. + scores: + type: object + properties: + host: + type: array + items: + $ref: '../common/common.schema.yaml#/components/schemas/EntityRiskScoreRecord' + description: A list of host risk scores + user: + type: array + items: + $ref: '../common/common.schema.yaml#/components/schemas/EntityRiskScoreRecord' + description: A list of user risk scores + refresh: + type: string + enum: [wait_for] + description: If 'wait_for' the request will wait for the index refresh. diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/common.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/common.gen.ts deleted file mode 100644 index 3e4f3155ca70..000000000000 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/common.gen.ts +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { z } from 'zod'; - -/* - * NOTICE: Do not edit this file manually. - * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. - * - * info: - * title: Risk Engine Common Schema - * version: 1.0.0 - */ - -export type AfterKeys = z.infer; -export const AfterKeys = z.object({ - host: z.object({}).catchall(z.string()).optional(), - user: z.object({}).catchall(z.string()).optional(), -}); - -/** - * The identifier of the Kibana data view to be used when generating risk scores. - */ -export type DataViewId = z.infer; -export const DataViewId = z.string(); - -/** - * An elasticsearch DSL filter object. Used to filter the risk inputs involved, which implicitly filters the risk scores themselves. See https://cloud.elastic.co/api/v1/api-docs/spec.json#/definitions/QueryContainer - */ -export type Filter = z.infer; -export const Filter = z.object({}); - -/** - * Specifies how many scores will be involved in a given calculation. Note that this value is per `identifier_type`, i.e. a value of 10 will calculate 10 host scores and 10 user scores, if available. To avoid missed data, keep this value consistent while paginating through scores. - */ -export type PageSize = z.infer; -export const PageSize = z.number().default(1000); - -export type KibanaDate = z.infer; -export const KibanaDate = z.union([z.string(), z.string().datetime(), z.string()]); - -/** - * Defines the time period on which risk inputs will be filtered. - */ -export type DateRange = z.infer; -export const DateRange = z.object({ - start: KibanaDate, - end: KibanaDate, -}); - -export type IdentifierType = z.infer; -export const IdentifierType = z.enum(['host', 'user']); -export type IdentifierTypeEnum = typeof IdentifierType.enum; -export const IdentifierTypeEnum = IdentifierType.enum; - -/** - * A generic representation of a document contributing to a Risk Score. - */ -export type RiskScoreInput = z.infer; -export const RiskScoreInput = z.object({ - /** - * The unique identifier (`_id`) of the original source document - */ - id: z.string().optional(), - /** - * The unique index (`_index`) of the original source document - */ - index: z.string().optional(), - /** - * The risk category of the risk input document. - */ - category: z.string().optional(), - /** - * A human-readable description of the risk input document. - */ - description: z.string().optional(), - /** - * The weighted risk score of the risk input document. - */ - risk_score: z.number().min(0).max(100).optional(), - /** - * The @timestamp of the risk input document. - */ - timestamp: z.string().optional(), -}); - -export type RiskScore = z.infer; -export const RiskScore = z.object({ - /** - * The time at which the risk score was calculated. - */ - '@timestamp': z.string().datetime(), - /** - * The identifier field defining this risk score. Coupled with `id_value`, uniquely identifies the entity being scored. - */ - id_field: z.string(), - /** - * The identifier value defining this risk score. Coupled with `id_field`, uniquely identifies the entity being scored. - */ - id_value: z.string(), - /** - * Lexical description of the entity's risk. - */ - calculated_level: z.string(), - /** - * The raw numeric value of the given entity's risk score. - */ - calculated_score: z.number(), - /** - * The normalized numeric value of the given entity's risk score. Useful for comparing with other entities. - */ - calculated_score_norm: z.number().min(0).max(100), - /** - * The contribution of Category 1 to the overall risk score (`calculated_score_norm`). Category 1 contains Detection Engine Alerts. - */ - category_1_score: z.number(), - /** - * The number of risk input documents that contributed to the Category 1 score (`category_1_score`). - */ - category_1_count: z.number(), - /** - * The contribution of Category 2 to the overall risk score (`calculated_score_norm`). Category 2 contains context from external sources. - */ - category_2_score: z.number().optional(), - /** - * The number of risk input documents that contributed to the Category 2 score (`category_2_score`). - */ - category_2_count: z.number().optional(), - /** - * The designated criticality level of the entity. Possible values are `low_impact`, `medium_impact`, `high_impact`, and `extreme_impact`. - */ - criticality_level: z.string().optional(), - /** - * The numeric modifier corresponding to the criticality level of the entity, which is used as an input to the risk score calculation. - */ - criticality_modifier: z.number().optional(), - /** - * A list of the highest-risk documents contributing to this risk score. Useful for investigative purposes. - */ - inputs: z.array(RiskScoreInput), -}); - -/** - * Configuration used to tune risk scoring. Weights can be used to change the score contribution of risk inputs for hosts and users at both a global level and also for Risk Input categories (e.g. 'category_1'). - */ -export type RiskScoreWeight = z.infer; -export const RiskScoreWeight = z.object({ - type: z.string(), - value: z.string().optional(), - host: z.number().min(0).max(1).optional(), - user: z.number().min(0).max(1).optional(), -}); - -/** - * A list of weights to be applied to the scoring calculation. - */ -export type RiskScoreWeights = z.infer; -export const RiskScoreWeights = z.array(RiskScoreWeight); - -export type RiskEngineInitStep = z.infer; -export const RiskEngineInitStep = z.object({ - type: z.string(), - success: z.boolean(), - error: z.string().optional(), -}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/common.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/common.schema.yaml deleted file mode 100644 index b208050fb4b2..000000000000 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/common.schema.yaml +++ /dev/null @@ -1,224 +0,0 @@ -openapi: 3.0.0 -info: - title: Risk Engine Common Schema - description: Common schema for Risk Engine APIs - version: 1.0.0 - -paths: { } - -components: - schemas: - - AfterKeys: - type: object - properties: - host: - type: object - additionalProperties: - type: string - user: - type: object - additionalProperties: - type: string - example: - host: - 'host.name': 'example.host' - user: - 'user.name': 'example_user_name' - - DataViewId: - description: The identifier of the Kibana data view to be used when generating risk scores. - example: security-solution-default - type: string - - Filter: - type: object - description: An elasticsearch DSL filter object. Used to filter the risk inputs involved, which implicitly filters the risk scores themselves. See https://cloud.elastic.co/api/v1/api-docs/spec.json#/definitions/QueryContainer - - PageSize: - description: Specifies how many scores will be involved in a given calculation. Note that this value is per `identifier_type`, i.e. a value of 10 will calculate 10 host scores and 10 user scores, if available. To avoid missed data, keep this value consistent while paginating through scores. - default: 1000 - type: number - - KibanaDate: - oneOf: - - type: string - format: date - - type: string - format: date-time - - type: string - format: datemath - example: '2017-07-21T17:32:28Z' - - DateRange: - description: Defines the time period on which risk inputs will be filtered. - type: object - required: - - start - - end - properties: - start: - $ref: '#/components/schemas/KibanaDate' - end: - $ref: '#/components/schemas/KibanaDate' - - IdentifierType: - type: string - enum: - - host - - user - - RiskScoreInput: - description: A generic representation of a document contributing to a Risk Score. - type: object - properties: - id: - type: string - example: 91a93376a507e86cfbf282166275b89f9dbdb1f0be6c8103c6ff2909ca8e1a1c - description: The unique identifier (`_id`) of the original source document - index: - type: string - example: .internal.alerts-security.alerts-default-000001 - description: The unique index (`_index`) of the original source document - category: - type: string - example: category_1 - description: The risk category of the risk input document. - description: - type: string - example: 'Generated from Detection Engine Rule: Malware Prevention Alert' - description: A human-readable description of the risk input document. - risk_score: - type: number - format: double - minimum: 0 - maximum: 100 - description: The weighted risk score of the risk input document. - timestamp: - type: string - example: '2017-07-21T17:32:28Z' - description: The @timestamp of the risk input document. - - - RiskScore: - type: object - required: - - '@timestamp' - - id_field - - id_value - - calculated_level - - calculated_score - - calculated_score_norm - - category_1_score - - category_1_count - - inputs - properties: - '@timestamp': - type: string - format: 'date-time' - example: '2017-07-21T17:32:28Z' - description: The time at which the risk score was calculated. - id_field: - type: string - example: 'host.name' - description: The identifier field defining this risk score. Coupled with `id_value`, uniquely identifies the entity being scored. - id_value: - type: string - example: 'example.host' - description: The identifier value defining this risk score. Coupled with `id_field`, uniquely identifies the entity being scored. - calculated_level: - type: string - example: 'Critical' - description: Lexical description of the entity's risk. - calculated_score: - type: number - format: double - description: The raw numeric value of the given entity's risk score. - calculated_score_norm: - type: number - format: double - minimum: 0 - maximum: 100 - description: The normalized numeric value of the given entity's risk score. Useful for comparing with other entities. - category_1_score: - type: number - format: double - description: The contribution of Category 1 to the overall risk score (`calculated_score_norm`). Category 1 contains Detection Engine Alerts. - category_1_count: - type: number - format: integer - description: The number of risk input documents that contributed to the Category 1 score (`category_1_score`). - category_2_score: - type: number - format: double - description: The contribution of Category 2 to the overall risk score (`calculated_score_norm`). Category 2 contains context from external sources. - category_2_count: - type: number - format: integer - description: The number of risk input documents that contributed to the Category 2 score (`category_2_score`). - criticality_level: - type: string - example: extreme_impact - description: The designated criticality level of the entity. Possible values are `low_impact`, `medium_impact`, `high_impact`, and `extreme_impact`. - criticality_modifier: - type: number - format: double - description: The numeric modifier corresponding to the criticality level of the entity, which is used as an input to the risk score calculation. - inputs: - type: array - description: A list of the highest-risk documents contributing to this risk score. Useful for investigative purposes. - items: - $ref: '#/components/schemas/RiskScoreInput' - - RiskScoreWeight: - description: "Configuration used to tune risk scoring. Weights can be used to change the score contribution of risk inputs for hosts and users at both a global level and also for Risk Input categories (e.g. 'category_1')." - type: object - required: - - type - properties: - type: - type: string - value: - type: string - host: - type: number - format: double - minimum: 0 - maximum: 1 - user: - type: number - format: double - minimum: 0 - maximum: 1 - example: - type: 'risk_category' - value: 'category_1' - host: 0.8 - user: 0.4 - - RiskScoreWeights: - description: 'A list of weights to be applied to the scoring calculation.' - type: array - items: - $ref: '#/components/schemas/RiskScoreWeight' - example: - - type: 'risk_category' - value: 'category_1' - host: 0.8 - user: 0.4 - - type: 'global_identifier' - host: 0.5 - user: 0.1 - - RiskEngineInitStep: - type: object - required: - - type - - success - properties: - type: - type: string - success: - type: boolean - error: - type: string diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_disable_route.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_disable_route.gen.ts new file mode 100644 index 000000000000..723f8ecfcf03 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_disable_route.gen.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { z } from 'zod'; + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Risk Scoring API + * version: 1 + */ + +export type RiskEngineDisableResponse = z.infer; +export const RiskEngineDisableResponse = z.object({ + success: z.boolean().optional(), +}); + +export type RiskEngineDisableErrorResponse = z.infer; +export const RiskEngineDisableErrorResponse = z.object({ + message: z.string(), + full_error: z.string(), +}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_disable_route_schema.yml b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_disable_route.schema.yaml similarity index 54% rename from x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_disable_route_schema.yml rename to x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_disable_route.schema.yaml index 71d4c9668181..b28d452a6883 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_disable_route_schema.yml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_disable_route.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: - version: 1.0.0 + version: '1' title: Risk Scoring API description: These APIs allow the consumer to manage Entity Risk Scores within Entity Analytics. @@ -27,6 +27,18 @@ paths: application/json: schema: $ref: '#/components/schemas/RiskEngineDisableResponse' + '400': + description: Task manager is unavailable + content: + application/json: + schema: + $ref: '../common/common.schema.yaml#/components/schemas/TaskManagerUnavailableResponse' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/RiskEngineDisableErrorResponse' components: schemas: @@ -35,3 +47,13 @@ components: properties: success: type: boolean + RiskEngineDisableErrorResponse: + type: object + required: + - message + - full_error + properties: + message: + type: string + full_error: + type: string diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_enable_route.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_enable_route.gen.ts new file mode 100644 index 000000000000..d2fadee44700 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_enable_route.gen.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { z } from 'zod'; + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Risk Scoring API + * version: 1 + */ + +export type RiskEngineEnableResponse = z.infer; +export const RiskEngineEnableResponse = z.object({ + success: z.boolean().optional(), +}); + +export type RiskEngineEnableErrorResponse = z.infer; +export const RiskEngineEnableErrorResponse = z.object({ + message: z.string(), + full_error: z.string(), +}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_enable_route_schema.yml b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_enable_route.schema.yaml similarity index 54% rename from x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_enable_route_schema.yml rename to x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_enable_route.schema.yaml index 42e4ec7af0a5..8f010537e379 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_enable_route_schema.yml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_enable_route.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: - version: 1.0.0 + version: '1' title: Risk Scoring API description: These APIs allow the consumer to manage Entity Risk Scores within Entity Analytics. @@ -27,6 +27,18 @@ paths: application/json: schema: $ref: '#/components/schemas/RiskEngineEnableResponse' + '400': + description: Task manager is unavailable + content: + application/json: + schema: + $ref: '../common/common.schema.yaml#/components/schemas/TaskManagerUnavailableResponse' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/RiskEngineEnableErrorResponse' components: schemas: @@ -35,3 +47,13 @@ components: properties: success: type: boolean + RiskEngineEnableErrorResponse: + type: object + required: + - message + - full_error + properties: + message: + type: string + full_error: + type: string diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_init_route.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_init_route.gen.ts new file mode 100644 index 000000000000..f66177372bde --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_init_route.gen.ts @@ -0,0 +1,37 @@ +/* + * 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 { z } from 'zod'; + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Risk Scoring API + * version: 1 + */ + +export type RiskEngineInitResult = z.infer; +export const RiskEngineInitResult = z.object({ + risk_engine_enabled: z.boolean(), + risk_engine_resources_installed: z.boolean(), + risk_engine_configuration_created: z.boolean(), + legacy_risk_engine_disabled: z.boolean(), + errors: z.array(z.string()), +}); + +export type RiskEngineInitResponse = z.infer; +export const RiskEngineInitResponse = z.object({ + result: RiskEngineInitResult, +}); + +export type RiskEngineInitErrorResponse = z.infer; +export const RiskEngineInitErrorResponse = z.object({ + message: z.string(), + full_error: z.string(), +}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_init_route.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_init_route.schema.yaml new file mode 100644 index 000000000000..893bd88a1f48 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_init_route.schema.yaml @@ -0,0 +1,80 @@ +openapi: 3.0.0 +info: + version: '1' + title: Risk Scoring API + description: These APIs allow the consumer to manage Entity Risk Scores within Entity Analytics. +servers: + - url: 'http://{kibana_host}:{port}' + variables: + kibana_host: + default: localhost + port: + default: '5601' + +paths: + /internal/risk_score/engine/init: + post: + summary: Initialize the Risk Engine + description: Initializes the Risk Engine by creating the necessary indices and mappings, removing old transforms, and starting the new risk engine + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '#/components/schemas/RiskEngineInitResponse' + '400': + description: Task manager is unavailable + content: + application/json: + schema: + $ref: '../common/common.schema.yaml#/components/schemas/TaskManagerUnavailableResponse' + default: + description: Unexpected error + content: + application/json: + schema: + $ref: '#/components/schemas/RiskEngineInitErrorResponse' + +components: + schemas: + RiskEngineInitResult: + type: object + required: + - risk_engine_enabled + - risk_engine_resources_installed + - risk_engine_configuration_created + - legacy_risk_engine_disabled + - errors + properties: + risk_engine_enabled: + type: boolean + risk_engine_resources_installed: + type: boolean + risk_engine_configuration_created: + type: boolean + legacy_risk_engine_disabled: + type: boolean + errors: + type: array + items: + type: string + + RiskEngineInitResponse: + type: object + required: + - result + properties: + result: + $ref: '#/components/schemas/RiskEngineInitResult' + + RiskEngineInitErrorResponse: + type: object + required: + - message + - full_error + properties: + message: + type: string + full_error: + type: string diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_init_route_schema.yml b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_init_route_schema.yml deleted file mode 100644 index daa6b3a33bb2..000000000000 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_init_route_schema.yml +++ /dev/null @@ -1,46 +0,0 @@ -openapi: 3.0.0 -info: - version: 1.0.0 - title: Risk Scoring API - description: These APIs allow the consumer to manage Entity Risk Scores within Entity Analytics. -servers: - - url: 'http://{kibana_host}:{port}' - variables: - kibana_host: - default: localhost - port: - default: '5601' - -paths: - /internal/risk_score/engine/init: - post: - summary: Initialize the Risk Engine - description: Initializes the Risk Engine by creating the necessary indices and mappings, removing old transforms, and starting the new risk engine - responses: - '200': - description: Successful response - content: - application/json: - schema: - $ref: '#/components/schemas/RiskEngineInitResponse' - -components: - schemas: - RiskEngineInitResponse: - type: object - properties: - result: - type: object - properties: - risk_engine_enabled: - type: boolean - risk_engine_resources_installed: - type: boolean - risk_engine_configuration_created: - type: boolean - legacy_risk_engine_disabled: - type: boolean - errors: - type: array - items: - type: string diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_settings_route.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_settings_route.gen.ts index 3fb57161c955..735de7e521c9 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_settings_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_settings_route.gen.ts @@ -13,7 +13,7 @@ import { z } from 'zod'; * * info: * title: Risk Scoring API - * version: 1.0.0 + * version: 1 */ import { DateRange } from '../common/common.gen'; diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_settings_route.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_settings_route.schema.yaml index cb67a3686822..2f48c76d58f5 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_settings_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_settings_route.schema.yaml @@ -1,6 +1,6 @@ openapi: 3.0.0 info: - version: 1.0.0 + version: '1' title: Risk Scoring API description: These APIs allow the consumer to manage Entity Risk Scores within Entity Analytics. servers: diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_status_route.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_status_route.gen.ts new file mode 100644 index 000000000000..bc94aff474e9 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_status_route.gen.ts @@ -0,0 +1,32 @@ +/* + * 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 { z } from 'zod'; + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Risk Scoring API + * version: 1 + */ + +export type RiskEngineStatus = z.infer; +export const RiskEngineStatus = z.enum(['NOT_INSTALLED', 'DISABLED', 'ENABLED']); +export type RiskEngineStatusEnum = typeof RiskEngineStatus.enum; +export const RiskEngineStatusEnum = RiskEngineStatus.enum; + +export type RiskEngineStatusResponse = z.infer; +export const RiskEngineStatusResponse = z.object({ + legacy_risk_engine_status: RiskEngineStatus, + risk_engine_status: RiskEngineStatus, + /** + * Indicates whether the maximum amount of risk engines has been reached + */ + is_max_amount_of_risk_engines_reached: z.boolean(), +}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_status_route_schema.yml b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_status_route.schema.yaml similarity index 89% rename from x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_status_route_schema.yml rename to x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_status_route.schema.yaml index 4aed23d48a0d..e182f9d70a9d 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_status_route_schema.yml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_status_route.schema.yaml @@ -1,6 +1,6 @@ openapi: 3.0.0 info: - version: 1.0.0 + version: '1' title: Risk Scoring API description: These APIs allow the consumer to manage Entity Risk Scores within Entity Analytics. servers: @@ -25,9 +25,18 @@ paths: $ref: '#/components/schemas/RiskEngineStatusResponse' components: schemas: - + RiskEngineStatus: + type: string + enum: + - 'NOT_INSTALLED' + - 'DISABLED' + - 'ENABLED' RiskEngineStatusResponse: type: object + required: + - legacy_risk_engine_status + - risk_engine_status + - is_max_amount_of_risk_engines_reached properties: legacy_risk_engine_status: $ref: '#/components/schemas/RiskEngineStatus' @@ -36,10 +45,3 @@ components: is_max_amount_of_risk_engines_reached: description: Indicates whether the maximum amount of risk engines has been reached type: boolean - - RiskEngineStatus: - type: string - enum: - - 'NOT_INSTALLED' - - 'DISABLED' - - 'ENABLED' diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/entity_calculation_route.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/entity_calculation_route.gen.ts index f4d7c393f6e7..2677043eb212 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/entity_calculation_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/entity_calculation_route.gen.ts @@ -13,10 +13,10 @@ import { z } from 'zod'; * * info: * title: Risk Scoring API - * version: 1.0.0 + * version: 1 */ -import { IdentifierType, RiskScore } from './common.gen'; +import { IdentifierType, EntityRiskScoreRecord } from '../common/common.gen'; export type RiskScoresEntityCalculationRequest = z.infer; export const RiskScoresEntityCalculationRequest = z.object({ @@ -39,5 +39,5 @@ export type RiskScoresEntityCalculationResponse = z.infer< >; export const RiskScoresEntityCalculationResponse = z.object({ success: z.boolean(), - score: RiskScore.optional(), + score: EntityRiskScoreRecord.optional(), }); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/entity_calculation_route.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/entity_calculation_route.schema.yaml index d776f6363a01..9c9812570691 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/entity_calculation_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/entity_calculation_route.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.0 info: - version: 1.0.0 + version: '1' title: Risk Scoring API description: These APIs allow the consumer to manage Entity Risk Scores within Entity Analytics. @@ -50,11 +50,11 @@ components: example: 'my.host' identifier_type: description: Used to define the type of entity. - $ref: './common.schema.yaml#/components/schemas/IdentifierType' + $ref: '../common/common.schema.yaml#/components/schemas/IdentifierType' refresh: - type: string - enum: [wait_for] - description: If 'wait_for' the request will wait for the index refresh. + type: string + enum: [wait_for] + description: If 'wait_for' the request will wait for the index refresh. RiskScoresEntityCalculationResponse: type: object @@ -64,4 +64,4 @@ components: success: type: boolean score: - $ref: './common.schema.yaml#/components/schemas/RiskScore' + $ref: '../common/common.schema.yaml#/components/schemas/EntityRiskScoreRecord' diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/preview_route.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/preview_route.gen.ts new file mode 100644 index 000000000000..c4ea1192d24e --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/preview_route.gen.ts @@ -0,0 +1,85 @@ +/* + * 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 { z } from 'zod'; + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Risk Scoring API + * version: 1 + */ + +import { + DataViewId, + AfterKeys, + Filter, + PageSize, + IdentifierType, + DateRange, + RiskScoreWeights, + EntityRiskScoreRecord, +} from '../common/common.gen'; + +export type RiskScoresPreviewRequest = z.infer; +export const RiskScoresPreviewRequest = z.object({ + /** + * The identifier of the Kibana data view to be used when generating risk scores. If a data view is not found, the provided ID will be used as the query's index pattern instead. + */ + data_view_id: DataViewId, + /** + * Used to retrieve a specific "page" of risk scores. If unspecified, the first "page" of scores is returned. See also the `after_keys` key in a risk scores response. + */ + after_keys: AfterKeys.optional(), + /** + * If set to `true`, a `debug` key is added to the response, containing both the internal request and response with elasticsearch. + */ + debug: z.boolean().optional(), + /** + * An elasticsearch DSL filter object. Used to filter the data being scored, which implicitly filters the risk scores returned. + */ + filter: Filter.optional(), + page_size: PageSize.optional(), + /** + * Used to restrict the type of risk scores involved. If unspecified, both `host` and `user` scores will be returned. + */ + identifier_type: IdentifierType.optional(), + /** + * Defines the time period over which scores will be evaluated. If unspecified, a range of `[now, now-30d]` will be used. + */ + range: DateRange.optional(), + weights: RiskScoreWeights.optional(), +}); + +export type RiskScoresPreviewResponse = z.infer; +export const RiskScoresPreviewResponse = z.object({ + /** + * Used to obtain the next "page" of risk scores. See also the `after_keys` key in a risk scores request. If this key is empty, the calculation is complete. + */ + after_keys: AfterKeys, + /** + * Object containing debug information, particularly the internal request and response from elasticsearch + */ + debug: z + .object({ + request: z.string().optional(), + response: z.string().optional(), + }) + .optional(), + scores: z.object({ + /** + * A list of host risk scores + */ + host: z.array(EntityRiskScoreRecord).optional(), + /** + * A list of user risk scores + */ + user: z.array(EntityRiskScoreRecord).optional(), + }), +}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/preview_route_schema.yml b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/preview_route.schema.yaml similarity index 72% rename from x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/preview_route_schema.yml rename to x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/preview_route.schema.yaml index fc66d3ed882c..1e21523c5f1f 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/preview_route_schema.yml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/preview_route.schema.yaml @@ -1,6 +1,6 @@ openapi: 3.0.0 info: - version: 1.0.0 + version: '1' title: Risk Scoring API description: These APIs allow the consumer to manage Entity Risk Scores within Entity Analytics. servers: @@ -40,30 +40,28 @@ components: required: - data_view_id properties: - after_keys: - description: Used to retrieve a specific "page" of risk scores. If unspecified, the first "page" of scores is returned. See also the `after_keys` key in a risk scores response. - allOf: - - $ref: 'common.yml#/components/schemas/AfterKeys' data_view_id: - $ref: 'common.yml#/components/schemas/DataViewId' + $ref: '../common/common.schema.yaml#/components/schemas/DataViewId' description: The identifier of the Kibana data view to be used when generating risk scores. If a data view is not found, the provided ID will be used as the query's index pattern instead. + after_keys: + description: Used to retrieve a specific "page" of risk scores. If unspecified, the first "page" of scores is returned. See also the `after_keys` key in a risk scores response. + $ref: '../common/common.schema.yaml#/components/schemas/AfterKeys' debug: description: If set to `true`, a `debug` key is added to the response, containing both the internal request and response with elasticsearch. type: boolean filter: - $ref: 'common.yml#/components/schemas/Filter' + $ref: '../common/common.schema.yaml#/components/schemas/Filter' description: An elasticsearch DSL filter object. Used to filter the data being scored, which implicitly filters the risk scores returned. page_size: - $ref: 'common.yml#/components/schemas/PageSize' + $ref: '../common/common.schema.yaml#/components/schemas/PageSize' identifier_type: description: Used to restrict the type of risk scores involved. If unspecified, both `host` and `user` scores will be returned. - allOf: - - $ref: 'common.yml#/components/schemas/IdentifierType' + $ref: '../common/common.schema.yaml#/components/schemas/IdentifierType' range: - $ref: 'common.yml#/components/schemas/DateRange' + $ref: '../common/common.schema.yaml#/components/schemas/DateRange' description: Defines the time period over which scores will be evaluated. If unspecified, a range of `[now, now-30d]` will be used. weights: - $ref: 'common.yml#/components/schemas/RiskScoreWeights' + $ref: '../common/common.schema.yaml#/components/schemas/RiskScoreWeights' RiskScoresPreviewResponse: type: object @@ -74,7 +72,7 @@ components: after_keys: description: Used to obtain the next "page" of risk scores. See also the `after_keys` key in a risk scores request. If this key is empty, the calculation is complete. allOf: - - $ref: 'common.yml#/components/schemas/AfterKeys' + - $ref: '../common/common.schema.yaml#/components/schemas/AfterKeys' debug: description: Object containing debug information, particularly the internal request and response from elasticsearch type: object @@ -84,7 +82,15 @@ components: response: type: string scores: - type: array - description: A list of risk scores - items: - $ref: 'common.yml#/components/schemas/RiskScore' + type: object + properties: + host: + type: array + items: + $ref: '../common/common.schema.yaml#/components/schemas/EntityRiskScoreRecord' + description: A list of host risk scores + user: + type: array + items: + $ref: '../common/common.schema.yaml#/components/schemas/EntityRiskScoreRecord' + description: A list of user risk scores diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/after_keys.test.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/after_keys.test.ts deleted file mode 100644 index 46664cd992e8..000000000000 --- a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/after_keys.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { afterKeysSchema } from './after_keys'; - -describe('after_keys schema', () => { - it('allows an empty object', () => { - const payload = {}; - const decoded = afterKeysSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - it('allows a valid host key', () => { - const payload = { host: { 'host.name': 'hello' } }; - const decoded = afterKeysSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - it('allows a valid user key', () => { - const payload = { user: { 'user.name': 'hello' } }; - const decoded = afterKeysSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - it('allows both valid host and user keys', () => { - const payload = { user: { 'user.name': 'hello' }, host: { 'host.name': 'hello' } }; - const decoded = afterKeysSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - it('removes an unknown identifier key if used', () => { - const payload = { bad: 'key' }; - const decoded = afterKeysSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual({}); - }); -}); diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/after_keys.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/after_keys.ts deleted file mode 100644 index 5c6e7a22025c..000000000000 --- a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/after_keys.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import * as t from 'io-ts'; - -const afterKeySchema = t.record(t.string, t.string); -export type AfterKeySchema = t.TypeOf; -export type AfterKey = AfterKeySchema; - -export const afterKeysSchema = t.exact( - t.partial({ - host: afterKeySchema, - user: afterKeySchema, - }) -); -export type AfterKeysSchema = t.TypeOf; -export type AfterKeys = AfterKeysSchema; diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/index.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/index.ts index bb75e496f386..e352beb54590 100644 --- a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/index.ts +++ b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/index.ts @@ -5,7 +5,6 @@ * 2.0. */ -export * from './after_keys'; export * from './risk_weights'; export * from './identifier_types'; export * from './range'; diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/privileges.test.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/privileges.test.ts index 5c25db68c87a..e0111b3d6787 100644 --- a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/privileges.test.ts +++ b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/privileges.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { EntityAnalyticsPrivileges } from '../../api/entity_analytics/common'; +import type { EntityAnalyticsPrivileges } from '../../api/entity_analytics/asset_criticality/get_asset_criticality_privileges.gen'; import { getMissingRiskEnginePrivileges } from './privileges'; describe('getMissingRiskEnginePrivileges', () => { diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/privileges.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/privileges.ts index add0a0e56efc..a375f5cb9195 100644 --- a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/privileges.ts +++ b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/privileges.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { EntityAnalyticsPrivileges } from '../../api/entity_analytics/common'; +import type { EntityAnalyticsPrivileges } from '../../api/entity_analytics/asset_criticality/get_asset_criticality_privileges.gen'; import { RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES, RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES, diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_levels.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_levels.ts index d2e777ad710a..af3576b6c10a 100644 --- a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_levels.ts +++ b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_levels.ts @@ -5,26 +5,27 @@ * 2.0. */ -import { RiskLevels } from './types'; +import type { EntityRiskLevels } from '../../api/entity_analytics/common'; +import { EntityRiskLevelsEnum } from '../../api/entity_analytics/common'; export const RISK_LEVEL_RANGES = { - [RiskLevels.unknown]: { start: 0, stop: 20 }, - [RiskLevels.low]: { start: 20, stop: 40 }, - [RiskLevels.moderate]: { start: 40, stop: 70 }, - [RiskLevels.high]: { start: 70, stop: 90 }, - [RiskLevels.critical]: { start: 90, stop: 100 }, + [EntityRiskLevelsEnum.Unknown]: { start: 0, stop: 20 }, + [EntityRiskLevelsEnum.Low]: { start: 20, stop: 40 }, + [EntityRiskLevelsEnum.Moderate]: { start: 40, stop: 70 }, + [EntityRiskLevelsEnum.High]: { start: 70, stop: 90 }, + [EntityRiskLevelsEnum.Critical]: { start: 90, stop: 100 }, }; -export const getRiskLevel = (riskScore: number): RiskLevels => { - if (riskScore >= RISK_LEVEL_RANGES[RiskLevels.critical].start) { - return RiskLevels.critical; - } else if (riskScore >= RISK_LEVEL_RANGES[RiskLevels.high].start) { - return RiskLevels.high; - } else if (riskScore >= RISK_LEVEL_RANGES[RiskLevels.moderate].start) { - return RiskLevels.moderate; - } else if (riskScore >= RISK_LEVEL_RANGES[RiskLevels.low].start) { - return RiskLevels.low; +export const getRiskLevel = (riskScore: number): EntityRiskLevels => { + if (riskScore >= RISK_LEVEL_RANGES[EntityRiskLevelsEnum.Critical].start) { + return EntityRiskLevelsEnum.Critical; + } else if (riskScore >= RISK_LEVEL_RANGES[EntityRiskLevelsEnum.High].start) { + return EntityRiskLevelsEnum.High; + } else if (riskScore >= RISK_LEVEL_RANGES[EntityRiskLevelsEnum.Moderate].start) { + return EntityRiskLevelsEnum.Moderate; + } else if (riskScore >= RISK_LEVEL_RANGES[EntityRiskLevelsEnum.Low].start) { + return EntityRiskLevelsEnum.Low; } else { - return RiskLevels.unknown; + return EntityRiskLevelsEnum.Unknown; } }; diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_score_calculation/request_schema.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_score_calculation/request_schema.ts deleted file mode 100644 index c05ca782afac..000000000000 --- a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_score_calculation/request_schema.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import * as t from 'io-ts'; -import { afterKeysSchema } from '../after_keys'; -import { identifierTypeSchema } from '../identifier_types'; -import { riskWeightsSchema } from '../risk_weights/schema'; - -export const riskScoreCalculationRequestSchema = t.exact( - t.intersection([ - t.type({ - data_view_id: t.string, - identifier_type: identifierTypeSchema, - range: t.type({ - start: t.string, - end: t.string, - }), - }), - t.partial({ - after_keys: afterKeysSchema, - debug: t.boolean, - filter: t.unknown, - page_size: t.number, - weights: riskWeightsSchema, - }), - ]) -); diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_score_preview/request_schema.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_score_preview/request_schema.ts deleted file mode 100644 index 76ee6a303532..000000000000 --- a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_score_preview/request_schema.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import * as t from 'io-ts'; -import { afterKeysSchema } from '../after_keys'; -import { identifierTypeSchema } from '../identifier_types'; -import { rangeSchema } from '../range'; -import { riskWeightsSchema } from '../risk_weights/schema'; - -export const riskScorePreviewRequestSchema = t.exact( - t.intersection([ - t.type({ - data_view_id: t.string, - }), - t.partial({ - after_keys: afterKeysSchema, - debug: t.boolean, - filter: t.unknown, - page_size: t.number, - identifier_type: identifierTypeSchema, - range: rangeSchema, - weights: riskWeightsSchema, - }), - ]) -); -export type RiskScorePreviewRequestSchema = t.TypeOf; diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_weights/index.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_weights/index.ts index 8aa51f283cef..6cc0ccaa93a6 100644 --- a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_weights/index.ts +++ b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_weights/index.ts @@ -6,4 +6,3 @@ */ export * from './types'; -export type { RiskWeight, RiskWeights, GlobalRiskWeight, RiskCategoryRiskWeight } from './schema'; diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_weights/schema.test.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_weights/schema.test.ts deleted file mode 100644 index 60c83f47b57c..000000000000 --- a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_weights/schema.test.ts +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; -import { foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; - -import { riskWeightSchema } from './schema'; -import { RiskCategories, RiskWeightTypes } from './types'; - -describe('risk weight schema', () => { - let type: string; - - describe('allowed types', () => { - it('allows the global weight type', () => { - const payload = { - type: RiskWeightTypes.global, - host: 0.1, - }; - const decoded = riskWeightSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - it('allows the risk category weight type', () => { - const payload = { - type: RiskWeightTypes.global, - host: 0.1, - }; - const decoded = riskWeightSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - it('rejects an unknown weight type', () => { - const payload = { - type: 'unknown', - host: 0.1, - }; - const decoded = riskWeightSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors)).length).toBeGreaterThan(0); - expect(message.schema).toEqual({}); - }); - }); - - describe('conditional fields', () => { - describe('global weights', () => { - beforeEach(() => { - type = RiskWeightTypes.global; - }); - - it('rejects if neither host nor user weight are specified', () => { - const payload = { type }; - const decoded = riskWeightSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "host"', - 'Invalid value "undefined" supplied to "user"', - ]); - expect(message.schema).toEqual({}); - }); - - it('allows a single host weight', () => { - const payload = { type, host: 0.1 }; - const decoded = riskWeightSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - it('allows a single user weight', () => { - const payload = { type, user: 0.1 }; - const decoded = riskWeightSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - it('allows both a host and user weight', () => { - const payload = { type, host: 0.1, user: 0.5 }; - const decoded = riskWeightSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual({ type, host: 0.1, user: 0.5 }); - }); - - it('rejects a weight outside of 0-1', () => { - const payload = { type, user: 55 }; - const decoded = riskWeightSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toContain('Invalid value "55" supplied to "user"'); - expect(message.schema).toEqual({}); - }); - - it('removes extra keys if specified', () => { - const payload = { - type, - host: 0.1, - value: 'superfluous', - extra: 'even more', - }; - const decoded = riskWeightSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual({ type, host: 0.1 }); - }); - }); - - describe('risk category weights', () => { - beforeEach(() => { - type = RiskWeightTypes.riskCategory; - }); - - it('requires a value', () => { - const payload = { type, user: 0.1 }; - const decoded = riskWeightSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "value"', - ]); - expect(message.schema).toEqual({}); - }); - - it('rejects if neither host nor user weight are specified', () => { - const payload = { type, value: RiskCategories.category_1 }; - const decoded = riskWeightSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "host"', - 'Invalid value "undefined" supplied to "user"', - ]); - expect(message.schema).toEqual({}); - }); - - it('allows a single host weight', () => { - const payload = { type, value: RiskCategories.category_1, host: 0.1 }; - const decoded = riskWeightSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - it('allows a single user weight', () => { - const payload = { type, value: RiskCategories.category_1, user: 0.1 }; - const decoded = riskWeightSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - it('allows both a host and user weight', () => { - const payload = { type, value: RiskCategories.category_1, user: 0.1, host: 0.5 }; - const decoded = riskWeightSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - it('rejects a weight outside of 0-1', () => { - const payload = { type, value: RiskCategories.category_1, host: -5 }; - const decoded = riskWeightSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toContain('Invalid value "-5" supplied to "host"'); - expect(message.schema).toEqual({}); - }); - - it('removes extra keys if specified', () => { - const payload = { - type, - value: RiskCategories.category_1, - host: 0.1, - extra: 'even more', - }; - const decoded = riskWeightSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual({ type, value: RiskCategories.category_1, host: 0.1 }); - }); - - describe('allowed category values', () => { - it('allows the alerts type for a category', () => { - const payload = { - type, - value: RiskCategories.category_1, - host: 0.1, - }; - const decoded = riskWeightSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - it('rejects an unknown category value', () => { - const payload = { - type, - value: 'unknown', - host: 0.1, - }; - const decoded = riskWeightSchema.decode(payload); - const message = pipe(decoded, foldLeftRight); - - expect(getPaths(left(message.errors))).toContain( - 'Invalid value "unknown" supplied to "value"' - ); - expect(message.schema).toEqual({}); - }); - }); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_weights/schema.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_weights/schema.ts deleted file mode 100644 index 16a8a6da03ae..000000000000 --- a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_weights/schema.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import * as t from 'io-ts'; -import { NumberBetweenZeroAndOneInclusive } from '@kbn/securitysolution-io-ts-types'; - -import { fromEnum } from '../utils'; -import { RiskCategories, RiskWeightTypes } from './types'; - -const hostWeight = t.type({ - host: NumberBetweenZeroAndOneInclusive, -}); - -const userWeight = t.type({ - user: NumberBetweenZeroAndOneInclusive, -}); - -const identifierWeights = t.union([ - t.exact(t.intersection([hostWeight, userWeight])), - t.exact(t.intersection([hostWeight, t.partial({ user: t.undefined })])), - t.exact(t.intersection([userWeight, t.partial({ host: t.undefined })])), -]); - -const riskCategories = fromEnum('riskCategories', RiskCategories); - -const globalRiskWeightSchema = t.intersection([ - t.exact( - t.type({ - type: t.literal(RiskWeightTypes.global), - }) - ), - identifierWeights, -]); -export type GlobalRiskWeight = t.TypeOf; - -const riskCategoryRiskWeightSchema = t.intersection([ - t.exact( - t.type({ - type: t.literal(RiskWeightTypes.riskCategory), - value: riskCategories, - }) - ), - identifierWeights, -]); -export type RiskCategoryRiskWeight = t.TypeOf; - -export const riskWeightSchema = t.union([globalRiskWeightSchema, riskCategoryRiskWeightSchema]); -export type RiskWeightSchema = t.TypeOf; -export type RiskWeight = RiskWeightSchema; - -export const riskWeightsSchema = t.array(riskWeightSchema); -export type RiskWeightsSchema = t.TypeOf; -export type RiskWeights = RiskWeightsSchema; diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/types.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/types.ts index c496cdab418b..a6c429126e29 100644 --- a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/types.ts +++ b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/types.ts @@ -5,20 +5,13 @@ * 2.0. */ -import type { CriticalityLevel } from '../asset_criticality/types'; -import type { RiskCategories } from './risk_weights/types'; +import type { EntityRiskScoreRecord, RiskScoreInput } from '../../api/entity_analytics/common'; export enum RiskScoreEntity { host = 'host', user = 'user', } -export enum RiskEngineStatus { - NOT_INSTALLED = 'NOT_INSTALLED', - DISABLED = 'DISABLED', - ENABLED = 'ENABLED', -} - export interface InitRiskEngineResult { legacyRiskEngineDisabled: boolean; riskEngineResourcesInstalled: boolean; @@ -26,55 +19,16 @@ export interface InitRiskEngineResult { riskEngineEnabled: boolean; errors: string[]; } - -export interface EntityRiskInput { - id: string; - index: string; - category: RiskCategories; - description: string; - risk_score: string | number | undefined; - timestamp: string | undefined; - contribution_score?: number; -} - export interface EcsRiskScore { '@timestamp': string; host?: { name: string; - risk: Omit; + risk: Omit; }; user?: { name: string; - risk: Omit; + risk: Omit; }; } -export type RiskInputs = EntityRiskInput[]; - -/** - * The API response object representing a risk score - */ -export interface RiskScore { - '@timestamp': string; - id_field: string; - id_value: string; - criticality_level?: CriticalityLevel; - criticality_modifier?: number | undefined; - calculated_level: RiskLevels; - calculated_score: number; - calculated_score_norm: number; - category_1_score: number; - category_1_count: number; - category_2_score?: number; - category_2_count?: number; - notes: string[]; - inputs: RiskInputs; -} - -export enum RiskLevels { - unknown = 'Unknown', - low = 'Low', - moderate = 'Moderate', - high = 'High', - critical = 'Critical', -} +export type RiskInputs = RiskScoreInput[]; diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts index b2322ad64c57..255a76e23f9f 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts @@ -7,9 +7,9 @@ import type { IEsSearchResponse } from '@kbn/search-types'; +import { EntityRiskLevels, EntityRiskLevelsEnum } from '../../../../api/entity_analytics/common'; +import type { EntityRiskScoreRecord } from '../../../../api/entity_analytics/common'; import type { Inspect, Maybe, SortField } from '../../../common'; -import type { RiskScore } from '../../../../entity_analytics/risk_engine'; -import { RiskLevels as RiskSeverity } from '../../../../entity_analytics/risk_engine'; export interface HostsRiskScoreStrategyResponse extends IEsSearchResponse { inspect?: Maybe; @@ -23,12 +23,13 @@ export interface UsersRiskScoreStrategyResponse extends IEsSearchResponse { data: UserRiskScore[] | undefined; } -export interface RiskStats extends RiskScore { +export interface RiskStats extends EntityRiskScoreRecord { rule_risks: RuleRisk[]; multipliers: string[]; } -export { RiskSeverity }; +export const RiskSeverity = EntityRiskLevels.enum; +export type RiskSeverity = EntityRiskLevels; export interface HostRiskScore { '@timestamp': string; @@ -76,8 +77,8 @@ export interface RiskScoreItem { [RiskScoreFields.timestamp]: Maybe; - [RiskScoreFields.hostRisk]: Maybe; - [RiskScoreFields.userRisk]: Maybe; + [RiskScoreFields.hostRisk]: Maybe; + [RiskScoreFields.userRisk]: Maybe; [RiskScoreFields.hostRiskScore]: Maybe; [RiskScoreFields.userRiskScore]: Maybe; @@ -89,9 +90,9 @@ export const isUserRiskScore = (risk: HostRiskScore | UserRiskScore): risk is Us 'user' in risk; export const EMPTY_SEVERITY_COUNT = { - [RiskSeverity.critical]: 0, - [RiskSeverity.high]: 0, - [RiskSeverity.low]: 0, - [RiskSeverity.moderate]: 0, - [RiskSeverity.unknown]: 0, + [EntityRiskLevelsEnum.Critical]: 0, + [EntityRiskLevelsEnum.High]: 0, + [EntityRiskLevelsEnum.Low]: 0, + [EntityRiskLevelsEnum.Moderate]: 0, + [EntityRiskLevelsEnum.Unknown]: 0, }; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts index 225ae72c5761..9903f6eb2ef7 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts @@ -6,12 +6,23 @@ */ import { useMemo } from 'react'; +import type { RiskEngineDisableResponse } from '../../../common/api/entity_analytics/risk_engine/engine_disable_route.gen'; +import type { RiskEngineStatusResponse } from '../../../common/api/entity_analytics/risk_engine/engine_status_route.gen'; +import type { RiskEngineInitResponse } from '../../../common/api/entity_analytics/risk_engine/engine_init_route.gen'; +import type { RiskEngineEnableResponse } from '../../../common/api/entity_analytics/risk_engine/engine_enable_route.gen'; +import type { + RiskScoresPreviewRequest, + RiskScoresPreviewResponse, +} from '../../../common/api/entity_analytics/risk_engine/preview_route.gen'; import type { RiskScoresEntityCalculationRequest, RiskScoresEntityCalculationResponse, } from '../../../common/api/entity_analytics/risk_engine/entity_calculation_route.gen'; import type { AssetCriticalityCsvUploadResponse } from '../../../common/entity_analytics/asset_criticality/types'; -import type { AssetCriticalityRecord } from '../../../common/api/entity_analytics/asset_criticality'; +import type { + AssetCriticalityRecord, + EntityAnalyticsPrivileges, +} from '../../../common/api/entity_analytics/asset_criticality'; import type { RiskScoreEntity } from '../../../common/search_strategy'; import { RISK_ENGINE_STATUS_URL, @@ -27,16 +38,6 @@ import { ASSET_CRITICALITY_CSV_UPLOAD_URL, RISK_SCORE_ENTITY_CALCULATION_URL, } from '../../../common/constants'; - -import type { - CalculateScoresResponse, - EnableRiskEngineResponse, - GetRiskEngineStatusResponse, - InitRiskEngineResponse, - DisableRiskEngineResponse, -} from '../../../server/lib/entity_analytics/types'; -import type { RiskScorePreviewRequestSchema } from '../../../common/entity_analytics/risk_engine/risk_score_preview/request_schema'; -import type { EntityAnalyticsPrivileges } from '../../../common/api/entity_analytics/common'; import type { RiskEngineSettingsResponse } from '../../../common/api/entity_analytics/risk_engine'; import type { SnakeToCamelCase } from '../common/utils'; import { useKibana } from '../../common/lib/kibana/kibana_react'; @@ -56,9 +57,9 @@ export const useEntityAnalyticsRoutes = () => { params, }: { signal?: AbortSignal; - params: RiskScorePreviewRequestSchema; + params: RiskScoresPreviewRequest; }) => - http.fetch(RISK_SCORE_PREVIEW_URL, { + http.fetch(RISK_SCORE_PREVIEW_URL, { version: '1', method: 'POST', body: JSON.stringify(params), @@ -69,7 +70,7 @@ export const useEntityAnalyticsRoutes = () => { * Fetches risks engine status */ const fetchRiskEngineStatus = ({ signal }: { signal?: AbortSignal }) => - http.fetch(RISK_ENGINE_STATUS_URL, { + http.fetch(RISK_ENGINE_STATUS_URL, { version: '1', method: 'GET', signal, @@ -79,7 +80,7 @@ export const useEntityAnalyticsRoutes = () => { * Init risk score engine */ const initRiskEngine = () => - http.fetch(RISK_ENGINE_INIT_URL, { + http.fetch(RISK_ENGINE_INIT_URL, { version: '1', method: 'POST', }); @@ -88,7 +89,7 @@ export const useEntityAnalyticsRoutes = () => { * Enable risk score engine */ const enableRiskEngine = () => - http.fetch(RISK_ENGINE_ENABLE_URL, { + http.fetch(RISK_ENGINE_ENABLE_URL, { version: '1', method: 'POST', }); @@ -97,7 +98,7 @@ export const useEntityAnalyticsRoutes = () => { * Disable risk score engine */ const disableRiskEngine = () => - http.fetch(RISK_ENGINE_DISABLE_URL, { + http.fetch(RISK_ENGINE_DISABLE_URL, { version: '1', method: 'POST', }); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_calculate_entity_risk_score.test.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_calculate_entity_risk_score.test.ts index 106fb9404372..d54663c74cda 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_calculate_entity_risk_score.test.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_calculate_entity_risk_score.test.ts @@ -9,14 +9,14 @@ import { act, renderHook } from '@testing-library/react-hooks'; import { TestProviders } from '../../../common/mock'; import { RiskScoreEntity } from '../../../../common/search_strategy'; import { useCalculateEntityRiskScore } from './use_calculate_entity_risk_score'; -import { RiskEngineStatus } from '../../../../common/entity_analytics/risk_engine'; import { waitFor } from '@testing-library/react'; +import { RiskEngineStatusEnum } from '../../../../common/api/entity_analytics/risk_engine/engine_status_route.gen'; const enabledRiskEngineStatus = { - risk_engine_status: RiskEngineStatus.ENABLED, + risk_engine_status: RiskEngineStatusEnum.ENABLED, }; const disabledRiskEngineStatus = { - risk_engine_status: RiskEngineStatus.DISABLED, + risk_engine_status: RiskEngineStatusEnum.DISABLED, }; const mockUseRiskEngineStatus = jest.fn(); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_calculate_entity_risk_score.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_calculate_entity_risk_score.ts index ff1eb5c46a70..d836c71b141a 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_calculate_entity_risk_score.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_calculate_entity_risk_score.ts @@ -9,10 +9,11 @@ import { useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { useMutation } from '@tanstack/react-query'; +import { RiskEngineStatusEnum } from '../../../../common/api/entity_analytics/risk_engine/engine_status_route.gen'; import { useEntityAnalyticsRoutes } from '../api'; import { useAppToasts } from '../../../common/hooks/use_app_toasts'; import { useRiskEngineStatus } from './use_risk_engine_status'; -import { RiskScoreEntity, RiskEngineStatus } from '../../../../common/entity_analytics/risk_engine'; +import { RiskScoreEntity } from '../../../../common/entity_analytics/risk_engine'; export const useCalculateEntityRiskScore = ( identifierType: RiskScoreEntity, @@ -41,7 +42,7 @@ export const useCalculateEntityRiskScore = ( }); const calculateEntityRiskScoreCb = useCallback(async () => { - if (riskEngineStatus?.risk_engine_status === RiskEngineStatus.ENABLED) { + if (riskEngineStatus?.risk_engine_status === RiskEngineStatusEnum.ENABLED) { mutate({ identifier_type: identifierType, identifier, diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_disable_risk_engine_mutation.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_disable_risk_engine_mutation.ts index 9f601d191d44..e19cf94fc379 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_disable_risk_engine_mutation.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_disable_risk_engine_mutation.ts @@ -6,12 +6,13 @@ */ import type { UseMutationOptions } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query'; +import type { TaskManagerUnavailableResponse } from '../../../../common/api/entity_analytics/common'; +import type { + RiskEngineDisableErrorResponse, + RiskEngineDisableResponse, +} from '../../../../common/api/entity_analytics/risk_engine/engine_disable_route.gen'; import { useEntityAnalyticsRoutes } from '../api'; import { useInvalidateRiskEngineStatusQuery } from './use_risk_engine_status'; -import type { - EnableRiskEngineResponse, - EnableDisableRiskEngineErrorResponse, -} from '../../../../server/lib/entity_analytics/types'; export const DISABLE_RISK_ENGINE_MUTATION_KEY = ['POST', 'DISABLE_RISK_ENGINE']; @@ -19,18 +20,18 @@ export const useDisableRiskEngineMutation = (options?: UseMutationOptions<{}>) = const invalidateRiskEngineStatusQuery = useInvalidateRiskEngineStatusQuery(); const { disableRiskEngine } = useEntityAnalyticsRoutes(); - return useMutation( - () => disableRiskEngine(), - { - ...options, - mutationKey: DISABLE_RISK_ENGINE_MUTATION_KEY, - onSettled: (...args) => { - invalidateRiskEngineStatusQuery(); + return useMutation< + RiskEngineDisableResponse, + { body: RiskEngineDisableErrorResponse | TaskManagerUnavailableResponse } + >(() => disableRiskEngine(), { + ...options, + mutationKey: DISABLE_RISK_ENGINE_MUTATION_KEY, + onSettled: (...args) => { + invalidateRiskEngineStatusQuery(); - if (options?.onSettled) { - options.onSettled(...args); - } - }, - } - ); + if (options?.onSettled) { + options.onSettled(...args); + } + }, + }); }; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_enable_risk_engine_mutation.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_enable_risk_engine_mutation.ts index 0f1a5995b765..658c4a5cdb18 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_enable_risk_engine_mutation.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_enable_risk_engine_mutation.ts @@ -6,29 +6,30 @@ */ import type { UseMutationOptions } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query'; +import type { TaskManagerUnavailableResponse } from '../../../../common/api/entity_analytics/common'; +import type { + RiskEngineEnableErrorResponse, + RiskEngineEnableResponse, +} from '../../../../common/api/entity_analytics/risk_engine/engine_enable_route.gen'; import { useEntityAnalyticsRoutes } from '../api'; import { useInvalidateRiskEngineStatusQuery } from './use_risk_engine_status'; -import type { - EnableRiskEngineResponse, - EnableDisableRiskEngineErrorResponse, -} from '../../../../server/lib/entity_analytics/types'; export const ENABLE_RISK_ENGINE_MUTATION_KEY = ['POST', 'ENABLE_RISK_ENGINE']; export const useEnableRiskEngineMutation = (options?: UseMutationOptions<{}>) => { const invalidateRiskEngineStatusQuery = useInvalidateRiskEngineStatusQuery(); const { enableRiskEngine } = useEntityAnalyticsRoutes(); - return useMutation( - enableRiskEngine, - { - ...options, - mutationKey: ENABLE_RISK_ENGINE_MUTATION_KEY, - onSettled: (...args) => { - invalidateRiskEngineStatusQuery(); + return useMutation< + RiskEngineEnableResponse, + { body: RiskEngineEnableErrorResponse | TaskManagerUnavailableResponse } + >(enableRiskEngine, { + ...options, + mutationKey: ENABLE_RISK_ENGINE_MUTATION_KEY, + onSettled: (...args) => { + invalidateRiskEngineStatusQuery(); - if (options?.onSettled) { - options.onSettled(...args); - } - }, - } - ); + if (options?.onSettled) { + options.onSettled(...args); + } + }, + }); }; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_init_risk_engine_mutation.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_init_risk_engine_mutation.ts index aa8a19c4a7a6..67d94257e916 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_init_risk_engine_mutation.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_init_risk_engine_mutation.ts @@ -6,19 +6,24 @@ */ import type { UseMutationOptions } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query'; +import type { TaskManagerUnavailableResponse } from '../../../../common/api/entity_analytics/common'; +import type { + RiskEngineInitErrorResponse, + RiskEngineInitResponse, +} from '../../../../common/api/entity_analytics/risk_engine/engine_init_route.gen'; import { useEntityAnalyticsRoutes } from '../api'; import { useInvalidateRiskEngineStatusQuery } from './use_risk_engine_status'; -import type { - InitRiskEngineResponse, - InitRiskEngineError, -} from '../../../../server/lib/entity_analytics/types'; export const INIT_RISK_ENGINE_STATUS_KEY = ['POST', 'INIT_RISK_ENGINE']; export const useInitRiskEngineMutation = (options?: UseMutationOptions<{}>) => { const invalidateRiskEngineStatusQuery = useInvalidateRiskEngineStatusQuery(); const { initRiskEngine } = useEntityAnalyticsRoutes(); - return useMutation(() => initRiskEngine(), { + + return useMutation< + RiskEngineInitResponse, + { body: RiskEngineInitErrorResponse | TaskManagerUnavailableResponse } + >(() => initRiskEngine(), { ...options, mutationKey: INIT_RISK_ENGINE_STATUS_KEY, onSettled: (...args) => { diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_preview_risk_scores.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_preview_risk_scores.ts index 01e9c6ac8dfe..837358aa9617 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_preview_risk_scores.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_preview_risk_scores.ts @@ -6,18 +6,18 @@ */ import { useQuery } from '@tanstack/react-query'; import dateMath from '@kbn/datemath'; +import type { RiskScoresPreviewRequest } from '../../../../common/api/entity_analytics/risk_engine/preview_route.gen'; import { useEntityAnalyticsRoutes } from '../api'; -import type { RiskScorePreviewRequestSchema } from '../../../../common/entity_analytics/risk_engine/risk_score_preview/request_schema'; export const useRiskScorePreview = ({ data_view_id: dataViewId, range, filter, -}: RiskScorePreviewRequestSchema) => { +}: RiskScoresPreviewRequest) => { const { fetchRiskScorePreview } = useEntityAnalyticsRoutes(); return useQuery(['POST', 'FETCH_PREVIEW_RISK_SCORE', range, filter], async ({ signal }) => { - const params: RiskScorePreviewRequestSchema = { data_view_id: dataViewId }; + const params: RiskScoresPreviewRequest = { data_view_id: dataViewId }; if (range) { const startTime = dateMath.parse(range.start)?.utc().toISOString(); const endTime = dateMath diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_engine_status.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_engine_status.ts index bedd09c21f0f..8b88a3a0148f 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_engine_status.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_engine_status.ts @@ -6,8 +6,8 @@ */ import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useCallback } from 'react'; +import { RiskEngineStatusEnum } from '../../../../common/api/entity_analytics/risk_engine/engine_status_route.gen'; import { useEntityAnalyticsRoutes } from '../api'; -import { RiskEngineStatus } from '../../../../common/entity_analytics/risk_engine/types'; import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features'; const FETCH_RISK_ENGINE_STATUS = ['GET', 'FETCH_RISK_ENGINE_STATUS']; @@ -52,10 +52,10 @@ export const useRiskEngineStatus = () => { } const response = await fetchRiskEngineStatus({ signal }); const isUpdateAvailable = - response?.legacy_risk_engine_status === RiskEngineStatus.ENABLED && - response.risk_engine_status === RiskEngineStatus.NOT_INSTALLED; + response?.legacy_risk_engine_status === RiskEngineStatusEnum.ENABLED && + response.risk_engine_status === RiskEngineStatusEnum.NOT_INSTALLED; const isNewRiskScoreModuleInstalled = - response.risk_engine_status !== RiskEngineStatus.NOT_INSTALLED; + response.risk_engine_status !== RiskEngineStatusEnum.NOT_INSTALLED; return { isUpdateAvailable, isNewRiskScoreModuleInstalled, diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score_kpi.tsx b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score_kpi.tsx index 029b20e121d9..c25c2d126456 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score_kpi.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_score_kpi.tsx @@ -140,11 +140,11 @@ export const useRiskScoreKpi = ({ } return { - [RiskSeverity.unknown]: result.kpiRiskScore[RiskSeverity.unknown] ?? 0, - [RiskSeverity.low]: result.kpiRiskScore[RiskSeverity.low] ?? 0, - [RiskSeverity.moderate]: result.kpiRiskScore[RiskSeverity.moderate] ?? 0, - [RiskSeverity.high]: result.kpiRiskScore[RiskSeverity.high] ?? 0, - [RiskSeverity.critical]: result.kpiRiskScore[RiskSeverity.critical] ?? 0, + [RiskSeverity.Unknown]: result.kpiRiskScore[RiskSeverity.Unknown] ?? 0, + [RiskSeverity.Low]: result.kpiRiskScore[RiskSeverity.Low] ?? 0, + [RiskSeverity.Moderate]: result.kpiRiskScore[RiskSeverity.Moderate] ?? 0, + [RiskSeverity.High]: result.kpiRiskScore[RiskSeverity.High] ?? 0, + [RiskSeverity.Critical]: result.kpiRiskScore[RiskSeverity.Critical] ?? 0, }; }, [result, loading, error]); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/common/utils.ts b/x-pack/plugins/security_solution/public/entity_analytics/common/utils.ts index 3756780e18ae..f3097fe46b9b 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/common/utils.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/common/utils.ts @@ -11,19 +11,19 @@ import { SEVERITY_COLOR } from '../../overview/components/detection_response/uti export { RISK_LEVEL_RANGES as RISK_SCORE_RANGES } from '../../../common/entity_analytics/risk_engine'; export const SEVERITY_UI_SORT_ORDER = [ - RiskSeverity.unknown, - RiskSeverity.low, - RiskSeverity.moderate, - RiskSeverity.high, - RiskSeverity.critical, + RiskSeverity.Unknown, + RiskSeverity.Low, + RiskSeverity.Moderate, + RiskSeverity.High, + RiskSeverity.Critical, ]; export const RISK_SEVERITY_COLOUR: { [k in RiskSeverity]: string } = { - [RiskSeverity.unknown]: euiLightVars.euiColorMediumShade, - [RiskSeverity.low]: SEVERITY_COLOR.low, - [RiskSeverity.moderate]: SEVERITY_COLOR.medium, - [RiskSeverity.high]: SEVERITY_COLOR.high, - [RiskSeverity.critical]: SEVERITY_COLOR.critical, + [RiskSeverity.Unknown]: euiLightVars.euiColorMediumShade, + [RiskSeverity.Low]: SEVERITY_COLOR.low, + [RiskSeverity.Moderate]: SEVERITY_COLOR.medium, + [RiskSeverity.High]: SEVERITY_COLOR.high, + [RiskSeverity.Critical]: SEVERITY_COLOR.critical, }; type SnakeToCamelCaseString = S extends `${infer T}_${infer U}` diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality/use_asset_criticality.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality/use_asset_criticality.ts index c56b39423632..9bd67dfed731 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality/use_asset_criticality.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/asset_criticality/use_asset_criticality.ts @@ -9,11 +9,11 @@ import type { UseMutationResult, UseQueryResult } from '@tanstack/react-query'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useUiSetting$ } from '@kbn/kibana-react-plugin/public'; import type { SecurityAppError } from '@kbn/securitysolution-t-grid'; +import type { EntityAnalyticsPrivileges } from '../../../../common/api/entity_analytics'; import type { CriticalityLevelWithUnassigned } from '../../../../common/entity_analytics/asset_criticality/types'; import { ENABLE_ASSET_CRITICALITY_SETTING } from '../../../../common/constants'; import { useHasSecurityCapability } from '../../../helper_hooks'; import type { AssetCriticalityRecord } from '../../../../common/api/entity_analytics/asset_criticality'; -import type { EntityAnalyticsPrivileges } from '../../../../common/api/entity_analytics/common'; import type { AssetCriticality, DeleteAssetCriticalityResponse } from '../../api/api'; import { useEntityAnalyticsRoutes } from '../../api/api'; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_header/index.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_header/index.test.tsx index 392656402c63..4a6d532d272c 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_header/index.test.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_header/index.test.tsx @@ -18,11 +18,11 @@ import { UsersTableType } from '../../../explore/users/store/model'; import type { SeverityCount } from '../severity/types'; const mockSeverityCount: SeverityCount = { - [RiskSeverity.low]: 1, - [RiskSeverity.high]: 1, - [RiskSeverity.moderate]: 1, - [RiskSeverity.unknown]: 1, - [RiskSeverity.critical]: 99, + [RiskSeverity.Low]: 1, + [RiskSeverity.High]: 1, + [RiskSeverity.Moderate]: 1, + [RiskSeverity.Unknown]: 1, + [RiskSeverity.Critical]: 99, }; jest.mock('../../../common/components/ml/hooks/use_ml_capabilities', () => ({ @@ -82,7 +82,7 @@ describe('Entity analytics header', () => { await waitFor(() => { expect(mockDispatch).toHaveBeenCalledWith( usersActions.updateUserRiskScoreSeverityFilter({ - severitySelection: [RiskSeverity.critical], + severitySelection: [RiskSeverity.Critical], }) ); @@ -109,7 +109,7 @@ describe('Entity analytics header', () => { await waitFor(() => { expect(mockDispatch).toHaveBeenCalledWith( hostsActions.updateHostRiskScoreSeverityFilter({ - severitySelection: [RiskSeverity.critical], + severitySelection: [RiskSeverity.Critical], hostsType: HostsType.page, }) ); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_header/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_header/index.tsx index 8a814a1ee5a2..1b880f624782 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_header/index.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_header/index.tsx @@ -88,7 +88,7 @@ export const EntityAnalyticsHeader = () => { onClick: () => { dispatch( hostsActions.updateHostRiskScoreSeverityFilter({ - severitySelection: [RiskSeverity.critical], + severitySelection: [RiskSeverity.Critical], hostsType: HostsType.page, }) ); @@ -111,7 +111,7 @@ export const EntityAnalyticsHeader = () => { onClick: () => { dispatch( usersActions.updateUserRiskScoreSeverityFilter({ - severitySelection: [RiskSeverity.critical], + severitySelection: [RiskSeverity.Critical], }) ); @@ -179,7 +179,7 @@ export const EntityAnalyticsHeader = () => { {hostsSeverityCount ? ( - + ) : ( '-' )} @@ -205,7 +205,7 @@ export const EntityAnalyticsHeader = () => { {usersSeverityCount ? ( - + ) : ( '-' )} diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/__mocks__/index.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/__mocks__/index.ts index 19f3a03e41c0..a4a8134f8480 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/__mocks__/index.ts @@ -8,9 +8,9 @@ import { RiskSeverity } from '../../../../../common/search_strategy/security_solution'; export const mockSeverityCount = { - [RiskSeverity.unknown]: 1, - [RiskSeverity.low]: 2, - [RiskSeverity.moderate]: 3, - [RiskSeverity.high]: 4, - [RiskSeverity.critical]: 5, + [RiskSeverity.Unknown]: 1, + [RiskSeverity.Low]: 2, + [RiskSeverity.Moderate]: 3, + [RiskSeverity.High]: 4, + [RiskSeverity.Critical]: 5, }; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/chart_content.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/chart_content.test.tsx index e346e999f8bd..ac9a224ffe60 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/chart_content.test.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/chart_content.test.tsx @@ -30,7 +30,7 @@ describe('ChartContent', () => { riskEntity: RiskScoreEntity.host, severityCount: undefined, timerange: { from: '2022-04-05T12:00:00.000Z', to: '2022-04-08T12:00:00.000Z' }, - selectedSeverity: [RiskSeverity.unknown], + selectedSeverity: [RiskSeverity.Unknown], }; beforeEach(() => { jest.clearAllMocks(); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/index.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/index.test.tsx index 057e250e339d..7de22b5a529b 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/index.test.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_analytics_risk_score/index.test.tsx @@ -34,11 +34,11 @@ jest.mock('../../../common/lib/kibana', () => { }); const mockSeverityCount: SeverityCount = { - [RiskSeverity.low]: 1, - [RiskSeverity.high]: 1, - [RiskSeverity.moderate]: 1, - [RiskSeverity.unknown]: 1, - [RiskSeverity.critical]: 1, + [RiskSeverity.Low]: 1, + [RiskSeverity.High]: 1, + [RiskSeverity.Moderate]: 1, + [RiskSeverity.Unknown]: 1, + [RiskSeverity.Critical]: 1, }; const mockUseQueryToggle = jest @@ -168,7 +168,7 @@ describe.each([RiskScoreEntity.host, RiskScoreEntity.user])( name: 'testUsername', risk: { rule_risks: [], - calculated_level: RiskSeverity.high, + calculated_level: RiskSeverity.High, calculated_score_norm: 75, multipliers: [], }, @@ -203,7 +203,7 @@ describe.each([RiskScoreEntity.host, RiskScoreEntity.user])( name, risk: { rule_risks: [], - calculated_level: RiskSeverity.high, + calculated_level: RiskSeverity.High, calculated_score_norm: 75, multipliers: [], }, @@ -246,7 +246,7 @@ describe.each([RiskScoreEntity.host, RiskScoreEntity.user])( name, risk: { rule_risks: [], - calculated_level: RiskSeverity.high, + calculated_level: RiskSeverity.High, calculated_score_norm: 75, multipliers: [], }, diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs.test.tsx index 6b632a826936..8524a2a6a26e 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs.test.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/entity_details_flyout/tabs/risk_inputs/risk_inputs.test.tsx @@ -44,7 +44,7 @@ const riskScore = { rule_risks: [], calculated_score_norm: 100, multipliers: [], - calculated_level: RiskSeverity.critical, + calculated_level: RiskSeverity.Critical, }, }, }; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_information/index.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_information/index.tsx index e0880bf56321..8596c7869edc 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_information/index.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_information/index.tsx @@ -69,11 +69,11 @@ const getRiskLevelTableColumns = (): Array { diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_donut_chart/use_risk_donut_chart_data.test.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_donut_chart/use_risk_donut_chart_data.test.ts index 933a9f98b5be..376417bb9f4d 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_donut_chart/use_risk_donut_chart_data.test.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_donut_chart/use_risk_donut_chart_data.test.ts @@ -13,11 +13,11 @@ import type { SeverityCount } from '../severity/types'; describe('useRiskDonutChartData', () => { it('returns the total', () => { const severityCount: SeverityCount = { - [RiskSeverity.low]: 1, - [RiskSeverity.high]: 2, - [RiskSeverity.moderate]: 3, - [RiskSeverity.unknown]: 4, - [RiskSeverity.critical]: 5, + [RiskSeverity.Low]: 1, + [RiskSeverity.High]: 2, + [RiskSeverity.Moderate]: 3, + [RiskSeverity.Unknown]: 4, + [RiskSeverity.Critical]: 5, }; const { result } = renderHook(() => useRiskDonutChartData(severityCount)); @@ -29,11 +29,11 @@ describe('useRiskDonutChartData', () => { it('returns all legends', () => { const severityCount: SeverityCount = { - [RiskSeverity.low]: 1, - [RiskSeverity.high]: 1, - [RiskSeverity.moderate]: 1, - [RiskSeverity.unknown]: 1, - [RiskSeverity.critical]: 1, + [RiskSeverity.Low]: 1, + [RiskSeverity.High]: 1, + [RiskSeverity.Moderate]: 1, + [RiskSeverity.Unknown]: 1, + [RiskSeverity.Critical]: 1, }; const { result } = renderHook(() => useRiskDonutChartData(severityCount)); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_enable_section.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_enable_section.tsx index 1a05128de67c..b5faebe500da 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_enable_section.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_enable_section.tsx @@ -30,12 +30,14 @@ import { } from '@elastic/eui'; import { LinkAnchor } from '@kbn/security-solution-navigation/links'; import { SecurityPageName } from '@kbn/security-solution-navigation'; +import type { RiskEngineStatus } from '../../../common/api/entity_analytics/risk_engine/engine_status_route.gen'; +import { RiskEngineStatusEnum } from '../../../common/api/entity_analytics/risk_engine/engine_status_route.gen'; import * as i18n from '../translations'; import { useRiskEngineStatus } from '../api/hooks/use_risk_engine_status'; import { useInitRiskEngineMutation } from '../api/hooks/use_init_risk_engine_mutation'; import { useEnableRiskEngineMutation } from '../api/hooks/use_enable_risk_engine_mutation'; import { useDisableRiskEngineMutation } from '../api/hooks/use_disable_risk_engine_mutation'; -import { RiskEngineStatus, MAX_SPACES_COUNT } from '../../../common/entity_analytics/risk_engine'; +import { MAX_SPACES_COUNT } from '../../../common/entity_analytics/risk_engine'; import { useAppToasts } from '../../common/hooks/use_app_toasts'; import { RiskInformationFlyout } from './risk_information'; import { useOnOpenCloseHandler } from '../../helper_hooks'; @@ -145,7 +147,7 @@ const RiskEngineHealth: React.FC<{ currentRiskEngineStatus?: RiskEngineStatus | if (!currentRiskEngineStatus) { return {'-'}; } - if (currentRiskEngineStatus === RiskEngineStatus.ENABLED) { + if (currentRiskEngineStatus === RiskEngineStatusEnum.ENABLED) { return {i18n.RISK_SCORE_MODULE_STATUS_ON}; } return {i18n.RISK_SCORE_MODULE_STATUS_OFF}; @@ -178,7 +180,7 @@ const RiskEngineStatusRow: React.FC<{ +const getRiskiestScores = (scores: EntityRiskScoreRecord[] = [], field: string) => scores ?.filter((item) => item?.id_field === field) ?.sort((a, b) => b?.calculated_score_norm - a?.calculated_score_norm) diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_preview_table.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_preview_table.tsx index db03ddde6376..febce7d9c23c 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_preview_table.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_preview_table.tsx @@ -9,24 +9,22 @@ import React from 'react'; import { EuiInMemoryTable } from '@elastic/eui'; import type { EuiBasicTableColumn } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import type { EntityRiskScoreRecord } from '../../../common/api/entity_analytics/common'; import type { RiskSeverity } from '../../../common/search_strategy'; import { RiskScoreLevel } from './severity/common'; import { HostDetailsLink, UserDetailsLink } from '../../common/components/links'; -import { - RiskScoreEntity, - type RiskScore as IRiskScore, -} from '../../../common/entity_analytics/risk_engine'; +import { RiskScoreEntity } from '../../../common/entity_analytics/risk_engine'; -type RiskScoreColumn = EuiBasicTableColumn & { - field: keyof IRiskScore; +type RiskScoreColumn = EuiBasicTableColumn & { + field: keyof EntityRiskScoreRecord; }; export const RiskScorePreviewTable = ({ items, type, }: { - items: IRiskScore[]; + items: EntityRiskScoreRecord[]; type: RiskScoreEntity; }) => { const columns: RiskScoreColumn[] = [ @@ -81,7 +79,7 @@ export const RiskScorePreviewTable = ({ ]; return ( - + data-test-subj={ type === RiskScoreEntity.host ? 'host-risk-preview-table' : 'user-risk-preview-table' } diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary.test.tsx index 9bb495118385..75722d781bbc 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary.test.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_summary.test.tsx @@ -19,7 +19,7 @@ describe.each([RiskScoreEntity.host, RiskScoreEntity.user])( 'RiskSummary entityType: %s', (riskEntity) => { it(`renders ${riskEntity} risk data`, () => { - const riskSeverity = RiskSeverity.low; + const riskSeverity = RiskSeverity.Low; const risk = { loading: false, isModuleEnabled: true, diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/severity/common/index.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/severity/common/index.test.tsx index c9b75129bb0a..245def2be69f 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/severity/common/index.test.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/severity/common/index.test.tsx @@ -31,11 +31,11 @@ describe('RiskScore', () => { it('renders critical severity risk score', () => { const { container } = render( - + ); - expect(container).toHaveTextContent(RiskSeverity.critical); + expect(container).toHaveTextContent(RiskSeverity.Critical); expect(EuiHealth as jest.Mock).toHaveBeenLastCalledWith( expect.objectContaining({ color: SEVERITY_COLOR.critical }), @@ -46,11 +46,11 @@ describe('RiskScore', () => { it('renders hight severity risk score', () => { const { container } = render( - + ); - expect(container).toHaveTextContent(RiskSeverity.high); + expect(container).toHaveTextContent(RiskSeverity.High); expect(EuiHealth as jest.Mock).toHaveBeenLastCalledWith( expect.objectContaining({ color: SEVERITY_COLOR.high }), @@ -61,11 +61,11 @@ describe('RiskScore', () => { it('renders moderate severity risk score', () => { const { container } = render( - + ); - expect(container).toHaveTextContent(RiskSeverity.moderate); + expect(container).toHaveTextContent(RiskSeverity.Moderate); expect(EuiHealth as jest.Mock).toHaveBeenLastCalledWith( expect.objectContaining({ color: SEVERITY_COLOR.medium }), @@ -76,11 +76,11 @@ describe('RiskScore', () => { it('renders low severity risk score', () => { const { container } = render( - + ); - expect(container).toHaveTextContent(RiskSeverity.low); + expect(container).toHaveTextContent(RiskSeverity.Low); expect(EuiHealth as jest.Mock).toHaveBeenLastCalledWith( expect.objectContaining({ color: SEVERITY_COLOR.low }), @@ -91,11 +91,11 @@ describe('RiskScore', () => { it('renders unknown severity risk score', () => { const { container } = render( - + ); - expect(container).toHaveTextContent(RiskSeverity.unknown); + expect(container).toHaveTextContent(RiskSeverity.Unknown); expect(EuiHealth as jest.Mock).toHaveBeenLastCalledWith( expect.objectContaining({ color: euiThemeVars.euiColorMediumShade }), @@ -106,7 +106,7 @@ describe('RiskScore', () => { it("doesn't render background-color when hideBackgroundColor is true", () => { const { queryByTestId } = render( - + ); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_filter_group.test.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_filter_group.test.tsx index 053338c67de6..0ea834a4bfc9 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_filter_group.test.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/severity/severity_filter_group.test.tsx @@ -33,11 +33,11 @@ describe('SeverityFilterGroup', () => { { { { risk: { rule_risks: [], calculated_score_norm: 71, - calculated_level: RiskSeverity.high, + calculated_level: RiskSeverity.High, multipliers: [], '@timestamp': '', id_field: '', diff --git a/x-pack/plugins/security_solution/public/entity_analytics/hooks/use_risk_contributing_alerts.ts b/x-pack/plugins/security_solution/public/entity_analytics/hooks/use_risk_contributing_alerts.ts index 65743d468ed2..3565843b120c 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/hooks/use_risk_contributing_alerts.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/hooks/use_risk_contributing_alerts.ts @@ -7,8 +7,8 @@ import { useEffect } from 'react'; import type { ALERT_RULE_NAME, ALERT_RULE_UUID } from '@kbn/rule-data-utils'; -import type { EntityRiskInput } from '../../../common/entity_analytics/risk_engine'; +import type { RiskScoreInput } from '../../../common/api/entity_analytics/common'; import { useQueryAlerts } from '../../detections/containers/detection_engine/alerts/use_query'; import { ALERTS_QUERY_NAMES } from '../../detections/containers/detection_engine/alerts/constants'; @@ -35,7 +35,7 @@ interface AlertHit { export interface InputAlert { alert: AlertData; - input: EntityRiskInput; + input: RiskScoreInput; _id: string; } diff --git a/x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/risk_score_summary.test.ts b/x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/risk_score_summary.test.ts index 6ef0abbf7769..47f5ef4c2275 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/risk_score_summary.test.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/lens_attributes/risk_score_summary.test.ts @@ -31,7 +31,7 @@ describe('getRiskScoreSummaryAttributes', () => { () => useLensAttributes({ lensAttributes: getRiskScoreSummaryAttributes({ - severity: RiskSeverity.low, + severity: RiskSeverity.Low, query: `user.name: test.user`, spaceId: 'default', riskEntity: RiskScoreEntity.user, @@ -48,7 +48,7 @@ describe('getRiskScoreSummaryAttributes', () => { () => useLensAttributes({ lensAttributes: getRiskScoreSummaryAttributes({ - severity: RiskSeverity.low, + severity: RiskSeverity.Low, query: `user.name: test.user`, spaceId: 'default', riskEntity: RiskScoreEntity.user, @@ -67,7 +67,7 @@ describe('getRiskScoreSummaryAttributes', () => { () => useLensAttributes({ lensAttributes: getRiskScoreSummaryAttributes({ - severity: RiskSeverity.low, + severity: RiskSeverity.Low, query, spaceId: 'default', riskEntity: RiskScoreEntity.user, diff --git a/x-pack/plugins/security_solution/public/explore/users/components/all_users/index.test.tsx b/x-pack/plugins/security_solution/public/explore/users/components/all_users/index.test.tsx index 7a97cc3515d8..da54aa8aa05c 100644 --- a/x-pack/plugins/security_solution/public/explore/users/components/all_users/index.test.tsx +++ b/x-pack/plugins/security_solution/public/explore/users/components/all_users/index.test.tsx @@ -89,7 +89,7 @@ describe('Users Table Component', () => { name: 'testUser', lastSeen: '2019-04-08T18:35:45.064Z', domain: 'test domain', - risk: RiskSeverity.critical, + risk: RiskSeverity.Critical, }, ]} fakeTotalCount={50} @@ -123,7 +123,7 @@ describe('Users Table Component', () => { name: 'testUser', lastSeen: '2019-04-08T18:35:45.064Z', domain: 'test domain', - risk: RiskSeverity.critical, + risk: RiskSeverity.Critical, }, ]} fakeTotalCount={50} diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.test.tsx index bc3105bedaba..4efc33e4a1ec 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/host_details.test.tsx @@ -120,7 +120,7 @@ const mockRiskScoreResponse = { const mockRelatedUsersResponse = { inspect: jest.fn(), refetch: jest.fn(), - relatedUsers: [{ user: 'test user', ip: ['100.XXX.XXX'], risk: RiskSeverity.low }], + relatedUsers: [{ user: 'test user', ip: ['100.XXX.XXX'], risk: RiskSeverity.Low }], loading: false, }; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.test.tsx index 03102e00f753..b8510c30c329 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/left/components/user_details.test.tsx @@ -117,7 +117,7 @@ const mockRiskScoreResponse = { const mockRelatedHostsResponse = { inspect: jest.fn(), refetch: jest.fn(), - relatedHosts: [{ host: 'test host', ip: ['100.XXX.XXX'], risk: RiskSeverity.low }], + relatedHosts: [{ host: 'test host', ip: ['100.XXX.XXX'], risk: RiskSeverity.Low }], loading: false, }; diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.test.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.test.tsx index 68c2b4868a31..fdda420616cc 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/host_details_left/index.test.tsx @@ -27,7 +27,7 @@ const riskScore: HostRiskScore = { category_1_score: 150, category_1_count: 1, multipliers: [], - calculated_level: RiskSeverity.critical, + calculated_level: RiskSeverity.Critical, inputs: [], notes: [], }, diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/mocks/index.ts b/x-pack/plugins/security_solution/public/flyout/entity_details/mocks/index.ts index 3ebd80489630..96543a7fba5b 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/mocks/index.ts +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/mocks/index.ts @@ -26,7 +26,7 @@ const userRiskScore: UserRiskScore = { rule_risks: [], calculated_score_norm: 70, multipliers: [], - calculated_level: RiskSeverity.high, + calculated_level: RiskSeverity.High, '@timestamp': '', id_field: '', id_value: '', @@ -60,7 +60,7 @@ const hostRiskScore: HostRiskScore = { rule_risks: [], calculated_score_norm: 70, multipliers: [], - calculated_level: RiskSeverity.high, + calculated_level: RiskSeverity.High, '@timestamp': '', id_field: '', id_value: '', diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/__mocks__/index.ts b/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/__mocks__/index.ts index da5f0f4c6440..d14f9024d64a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/__mocks__/index.ts @@ -21,7 +21,7 @@ const userRiskScore = { rule_risks: [], calculated_score_norm: 70, multipliers: [], - calculated_level: RiskSeverity.high, + calculated_level: RiskSeverity.High, }, }, alertsCount: 0, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.ts index f462584a60ea..e8f7f573868a 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.ts @@ -8,12 +8,9 @@ import type { Logger, ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; import type { TaskManagerStartContract } from '@kbn/task-manager-plugin/server'; import type { AuditLogger } from '@kbn/security-plugin-types-server'; +import { RiskEngineStatusEnum } from '../../../../common/api/entity_analytics/risk_engine/engine_status_route.gen'; import type { InitRiskEngineResult } from '../../../../common/entity_analytics/risk_engine'; -import { - RiskEngineStatus, - MAX_SPACES_COUNT, - RiskScoreEntity, -} from '../../../../common/entity_analytics/risk_engine'; +import { MAX_SPACES_COUNT, RiskScoreEntity } from '../../../../common/entity_analytics/risk_engine'; import { removeLegacyTransforms, getLegacyTransforms } from '../utils/transforms'; import { updateSavedObjectAttribute, @@ -199,7 +196,7 @@ export class RiskEngineDataClient { public async disableLegacyRiskEngine({ namespace }: { namespace: string }) { const legacyRiskEngineStatus = await this.getLegacyStatus({ namespace }); - if (legacyRiskEngineStatus === RiskEngineStatus.NOT_INSTALLED) { + if (legacyRiskEngineStatus === RiskEngineStatusEnum.NOT_INSTALLED) { return true; } @@ -221,17 +218,17 @@ export class RiskEngineDataClient { const newlegacyRiskEngineStatus = await this.getLegacyStatus({ namespace }); - return newlegacyRiskEngineStatus === RiskEngineStatus.NOT_INSTALLED; + return newlegacyRiskEngineStatus === RiskEngineStatusEnum.NOT_INSTALLED; } private async getCurrentStatus() { const configuration = await this.getConfiguration(); if (configuration) { - return configuration.enabled ? RiskEngineStatus.ENABLED : RiskEngineStatus.DISABLED; + return configuration.enabled ? RiskEngineStatusEnum.ENABLED : RiskEngineStatusEnum.DISABLED; } - return RiskEngineStatus.NOT_INSTALLED; + return RiskEngineStatusEnum.NOT_INSTALLED; } private async getIsMaxAmountOfRiskEnginesReached() { @@ -271,9 +268,9 @@ export class RiskEngineDataClient { }); if (transforms.length === 0) { - return RiskEngineStatus.NOT_INSTALLED; + return RiskEngineStatusEnum.NOT_INSTALLED; } - return RiskEngineStatus.ENABLED; + return RiskEngineStatusEnum.ENABLED; } } diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_privileges.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_privileges.test.ts index f46429271342..e689bcf9f3ca 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_privileges.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_privileges.test.ts @@ -4,7 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { EntityAnalyticsPrivileges } from '../../../../common/api/entity_analytics/common'; + +import type { EntityAnalyticsPrivileges } from '../../../../common/api/entity_analytics'; import { _getMissingPrivilegesMessage } from './risk_engine_privileges'; describe('_getMissingPrivilegesMessage', () => { diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_privileges.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_privileges.ts index 573f8147f8b9..56f6fb80fe85 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_privileges.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_privileges.ts @@ -14,7 +14,7 @@ import type { import { buildSiemResponse } from '@kbn/lists-plugin/server/routes'; import type { SecurityPluginStart } from '@kbn/security-plugin/server'; import { i18n } from '@kbn/i18n'; -import type { EntityAnalyticsPrivileges } from '../../../../common/api/entity_analytics/common'; +import type { EntityAnalyticsPrivileges } from '../../../../common/api/entity_analytics'; import type { SecuritySolutionPluginStartDependencies } from '../../../plugin_contract'; import type { SecuritySolutionRequestHandlerContext } from '../../../types'; import { diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.ts index 5a8d44ef057a..f1f0348a69e3 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.ts @@ -7,6 +7,7 @@ import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; import { transformError } from '@kbn/securitysolution-es-utils'; +import type { RiskEngineDisableResponse } from '../../../../../common/api/entity_analytics/risk_engine/engine_disable_route.gen'; import { RISK_ENGINE_DISABLE_URL, APP_ID } from '../../../../../common/constants'; import { TASK_MANAGER_UNAVAILABLE_ERROR } from './translations'; import { withRiskEnginePrivilegeCheck } from '../risk_engine_privileges'; @@ -70,7 +71,8 @@ export const riskEngineDisableRoute = ( try { await riskEngineClient.disableRiskEngine({ taskManager }); - return response.ok({ body: { success: true } }); + const body: RiskEngineDisableResponse = { success: true }; + return response.ok({ body }); } catch (e) { const error = transformError(e); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.ts index 237f10bc00ad..a4eed8701d1e 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.ts @@ -7,6 +7,7 @@ import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; import { transformError } from '@kbn/securitysolution-es-utils'; +import type { RiskEngineEnableResponse } from '../../../../../common/api/entity_analytics/risk_engine/engine_enable_route.gen'; import { RISK_ENGINE_ENABLE_URL, APP_ID } from '../../../../../common/constants'; import { TASK_MANAGER_UNAVAILABLE_ERROR } from './translations'; import { withRiskEnginePrivilegeCheck } from '../risk_engine_privileges'; @@ -68,7 +69,8 @@ export const riskEngineEnableRoute = ( try { await riskEngineClient.enableRiskEngine({ taskManager }); - return response.ok({ body: { success: true } }); + const body: RiskEngineEnableResponse = { success: true }; + return response.ok({ body }); } catch (e) { const error = transformError(e); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/init.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/init.ts index 0b0efda642fc..8360f3652a7f 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/init.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/init.ts @@ -7,9 +7,13 @@ import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; import { transformError } from '@kbn/securitysolution-es-utils'; +import type { + RiskEngineInitResponse, + RiskEngineInitResult, +} from '../../../../../common/api/entity_analytics/risk_engine/engine_init_route.gen'; import { RISK_ENGINE_INIT_URL, APP_ID } from '../../../../../common/constants'; import { TASK_MANAGER_UNAVAILABLE_ERROR } from './translations'; -import type { EntityAnalyticsRoutesDeps, InitRiskEngineResultResponse } from '../../types'; +import type { EntityAnalyticsRoutesDeps } from '../../types'; import { withRiskEnginePrivilegeCheck } from '../risk_engine_privileges'; import { RiskEngineAuditActions } from '../audit'; import { AUDIT_CATEGORY, AUDIT_OUTCOME, AUDIT_TYPE } from '../../audit'; @@ -60,7 +64,7 @@ export const riskEngineInitRoute = ( riskScoreDataClient, }); - const initResultResponse: InitRiskEngineResultResponse = { + const result: RiskEngineInitResult = { risk_engine_enabled: initResult.riskEngineEnabled, risk_engine_resources_installed: initResult.riskEngineResourcesInstalled, risk_engine_configuration_created: initResult.riskEngineConfigurationCreated, @@ -68,6 +72,10 @@ export const riskEngineInitRoute = ( errors: initResult.errors, }; + const initResponse: RiskEngineInitResponse = { + result, + }; + if ( !initResult.riskEngineEnabled || !initResult.riskEngineResourcesInstalled || @@ -76,13 +84,13 @@ export const riskEngineInitRoute = ( return siemResponse.error({ statusCode: 400, body: { - message: initResultResponse.errors.join('\n'), - full_error: initResultResponse, + message: result.errors.join('\n'), + full_error: result, }, bypassErrorFormat: true, }); } - return response.ok({ body: { result: initResultResponse } }); + return response.ok({ body: initResponse }); } catch (e) { const error = transformError(e); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/privileges.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/privileges.ts index d7343343a1c3..38b48aca7e5a 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/privileges.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/privileges.ts @@ -7,6 +7,7 @@ import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; import { transformError } from '@kbn/securitysolution-es-utils'; +import type { EntityAnalyticsPrivileges } from '../../../../../common/api/entity_analytics'; import { RISK_ENGINE_PRIVILEGES_URL, APP_ID } from '../../../../../common/constants'; import { AUDIT_CATEGORY, AUDIT_OUTCOME, AUDIT_TYPE } from '../../audit'; import { RiskScoreAuditActions } from '../../risk_score/audit'; @@ -31,7 +32,7 @@ export const riskEnginePrivilegesRoute = ( const [_, { security }] = await getStartServices(); const securitySolution = await context.securitySolution; - const body = await getUserRiskEnginePrivileges(request, security); + const body: EntityAnalyticsPrivileges = await getUserRiskEnginePrivileges(request, security); securitySolution.getAuditLogger()?.log({ message: 'User checked if they have the required privileges to configure the risk engine', diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/settings.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/settings.ts index e538ae660b6e..032114f7871b 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/settings.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/settings.ts @@ -7,6 +7,7 @@ import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; import { transformError } from '@kbn/securitysolution-es-utils'; +import type { RiskEngineSettingsResponse } from '../../../../../common/api/entity_analytics/risk_engine'; import { RISK_ENGINE_SETTINGS_URL, APP_ID } from '../../../../../common/constants'; import { AUDIT_CATEGORY, AUDIT_OUTCOME, AUDIT_TYPE } from '../../audit'; import type { EntityAnalyticsRoutesDeps } from '../../types'; @@ -42,10 +43,11 @@ export const riskEngineSettingsRoute = (router: EntityAnalyticsRoutesDeps['route if (!result) { throw new Error('Unable to get risk engine configuration'); } + const body: RiskEngineSettingsResponse = { + range: result.range, + }; return response.ok({ - body: { - range: result.range, - }, + body, }); } catch (e) { const error = transformError(e); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/status.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/status.ts index 46384eb776ae..331638dcb4ba 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/status.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/status.ts @@ -7,6 +7,7 @@ import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; import { transformError } from '@kbn/securitysolution-es-utils'; +import type { RiskEngineStatusResponse } from '../../../../../common/api/entity_analytics/risk_engine/engine_status_route.gen'; import { RISK_ENGINE_STATUS_URL, APP_ID } from '../../../../../common/constants'; import type { EntityAnalyticsRoutesDeps } from '../../types'; @@ -27,16 +28,18 @@ export const riskEngineStatusRoute = (router: EntityAnalyticsRoutesDeps['router' const spaceId = securitySolution.getSpaceId(); try { - const result = await riskEngineClient.getStatus({ - namespace: spaceId, - }); - return response.ok({ - body: { - risk_engine_status: result.riskEngineStatus, - legacy_risk_engine_status: result.legacyRiskEngineStatus, - is_max_amount_of_risk_engines_reached: result.isMaxAmountOfRiskEnginesReached, - }, - }); + const { riskEngineStatus, legacyRiskEngineStatus, isMaxAmountOfRiskEnginesReached } = + await riskEngineClient.getStatus({ + namespace: spaceId, + }); + + const body: RiskEngineStatusResponse = { + risk_engine_status: riskEngineStatus, + legacy_risk_engine_status: legacyRiskEngineStatus, + is_max_amount_of_risk_engines_reached: isMaxAmountOfRiskEnginesReached, + }; + + return response.ok({ body }); } catch (e) { const error = transformError(e); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/schema/risk_score_apis.yml b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/schema/risk_score_apis.yml deleted file mode 100644 index 02471038ddb3..000000000000 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/schema/risk_score_apis.yml +++ /dev/null @@ -1,485 +0,0 @@ -openapi: 3.0.0 -info: - version: 1.0.0 - title: Risk Scoring API - description: These APIs allow the consumer to manage Entity Risk Scores within Entity Analytics. -paths: - /calculate: - post: - summary: Trigger calculation of Risk Scores - description: Calculates and persists a segment of Risk Scores, returning details about the calculation. - requestBody: - description: Details about the Risk Scores being calculated - content: - application/json: - schema: - $ref: '#/components/schemas/RiskScoresCalculationRequest' - required: true - responses: - '200': - description: Successful response - content: - application/json: - schema: - $ref: '#/components/schemas/RiskScoresCalculationResponse' - '400': - description: Invalid request - /preview: - post: - summary: Preview the calculation of Risk Scores - description: Calculates and returns a list of Risk Scores, sorted by identifier_type and risk score. - requestBody: - description: Details about the Risk Scores being requested - content: - application/json: - schema: - $ref: '#/components/schemas/RiskScoresPreviewRequest' - required: true - responses: - '200': - description: Successful response - content: - application/json: - schema: - $ref: '#/components/schemas/RiskScoresPreviewResponse' - '400': - description: Invalid request - /engine/status: - get: - summary: Get the status of the Risk Engine - description: Returns the status of both the legacy transform-based risk engine, as well as the new risk engine - responses: - '200': - description: Successful response - content: - application/json: - schema: - $ref: '#/components/schemas/RiskEngineStatusResponse' - - /engine/init: - post: - summary: Initialize the Risk Engine - description: Initializes the Risk Engine by creating the necessary indices and mappings, removing old transforms, and starting the new risk engine - responses: - '200': - description: Successful response - content: - application/json: - schema: - $ref: '#/components/schemas/RiskEngineInitResponse' - /engine/enable: - post: - summary: Enable the Risk Engine - requestBody: - content: - application/json: {} - responses: - '200': - description: Successful response - content: - application/json: - schema: - $ref: '#/components/schemas/RiskEngineEnableResponse' - /engine/disable: - post: - summary: Disable the Risk Engine - requestBody: - content: - application/json: {} - responses: - '200': - description: Successful response - content: - application/json: - schema: - $ref: '#/components/schemas/RiskEngineDisableResponse' - /engine/privileges: - get: - summary: Check if the user has access to the risk engine - responses: - '200': - description: Successful response - content: - application/json: - schema: - $ref: '#/components/schemas/RiskEnginePrivilegesResponse' - /engine/settings: - get: - summary: Get the settings of the Risk Engine - responses: - '200': - description: Successful response - content: - application/json: - schema: - $ref: '#/components/schemas/RiskEngineSettingsResponse' - - -components: - schemas: - RiskScoresCalculationRequest: - type: object - required: - - data_view_id - - identifier_type - - range - properties: - after_keys: - description: Used to calculate a specific "page" of risk scores. If unspecified, the first "page" of scores is returned. See also the `after_keys` key in a risk scores response. - allOf: - - $ref: '#/components/schemas/AfterKeys' - data_view_id: - $ref: '#/components/schemas/DataViewId' - description: The identifier of the Kibana data view to be used when generating risk scores. If a data view is not found, the provided ID will be used as the query's index pattern instead. - debug: - description: If set to `true`, the internal ES requests/responses will be logged in Kibana. - type: boolean - filter: - $ref: '#/components/schemas/Filter' - description: An elasticsearch DSL filter object. Used to filter the data being scored, which implicitly filters the risk scores calculated. - page_size: - $ref: '#/components/schemas/PageSize' - identifier_type: - description: Used to restrict the type of risk scores calculated. - allOf: - - $ref: '#/components/schemas/IdentifierType' - range: - $ref: '#/components/schemas/DateRange' - description: Defines the time period over which scores will be evaluated. If unspecified, a range of `[now, now-30d]` will be used. - weights: - $ref: '#/components/schemas/RiskScoreWeights' - - RiskScoresPreviewRequest: - type: object - required: - - data_view_id - properties: - after_keys: - description: Used to retrieve a specific "page" of risk scores. If unspecified, the first "page" of scores is returned. See also the `after_keys` key in a risk scores response. - allOf: - - $ref: '#/components/schemas/AfterKeys' - data_view_id: - $ref: '#/components/schemas/DataViewId' - description: The identifier of the Kibana data view to be used when generating risk scores. If a data view is not found, the provided ID will be used as the query's index pattern instead. - debug: - description: If set to `true`, a `debug` key is added to the response, containing both the internal request and response with elasticsearch. - type: boolean - filter: - $ref: '#/components/schemas/Filter' - description: An elasticsearch DSL filter object. Used to filter the data being scored, which implicitly filters the risk scores returned. - page_size: - $ref: '#/components/schemas/PageSize' - identifier_type: - description: Used to restrict the type of risk scores involved. If unspecified, both `host` and `user` scores will be returned. - allOf: - - $ref: '#/components/schemas/IdentifierType' - range: - $ref: '#/components/schemas/DateRange' - description: Defines the time period over which scores will be evaluated. If unspecified, a range of `[now, now-30d]` will be used. - weights: - $ref: '#/components/schemas/RiskScoreWeights' - - RiskScoresCalculationResponse: - type: object - required: - - after_keys - - errors - - scores_written - properties: - after_keys: - description: Used to obtain the next "page" of risk scores. See also the `after_keys` key in a risk scores request. If this key is empty, the calculation is complete. - allOf: - - $ref: '#/components/schemas/AfterKeys' - errors: - type: array - description: A list of errors encountered during the calculation. - items: - type: string - scores_written: - type: number - format: integer - description: The number of risk scores persisted to elasticsearch. - - RiskScoresPreviewResponse: - type: object - required: - - after_keys - - scores - properties: - after_keys: - description: Used to obtain the next "page" of risk scores. See also the `after_keys` key in a risk scores request. If this key is empty, the calculation is complete. - allOf: - - $ref: '#/components/schemas/AfterKeys' - debug: - description: Object containing debug information, particularly the internal request and response from elasticsearch - type: object - properties: - request: - type: string - response: - type: string - scores: - type: array - description: A list of risk scores - items: - $ref: '#/components/schemas/RiskScore' - RiskEngineStatusResponse: - type: object - properties: - legacy_risk_engine_status: - $ref: '#/components/schemas/RiskEngineStatus' - risk_engine_status: - $ref: '#/components/schemas/RiskEngineStatus' - is_max_amount_of_risk_engines_reached: - description: Indicates whether the maximum amount of risk engines has been reached - type: boolean - RiskEngineInitResponse: - type: object - properties: - result: - type: object - properties: - risk_engine_enabled: - type: boolean - risk_engine_resources_installed: - type: boolean - risk_engine_configuration_created: - type: boolean - legacy_risk_engine_disabled: - type: boolean - errors: - type: array - items: - type: string - - - - RiskEngineEnableResponse: - type: object - properties: - success: - type: boolean - RiskEngineDisableResponse: - type: object - properties: - success: - type: boolean - - - AfterKeys: - type: object - properties: - host: - type: object - additionalProperties: - type: string - user: - type: object - additionalProperties: - type: string - example: - host: - 'host.name': 'example.host' - user: - 'user.name': 'example_user_name' - DataViewId: - description: The identifier of the Kibana data view to be used when generating risk scores. - example: security-solution-default - type: string - Filter: - description: An elasticsearch DSL filter object. Used to filter the risk inputs involved, which implicitly filters the risk scores themselves. - $ref: 'https://cloud.elastic.co/api/v1/api-docs/spec.json#/definitions/QueryContainer' - PageSize: - description: Specifies how many scores will be involved in a given calculation. Note that this value is per `identifier_type`, i.e. a value of 10 will calculate 10 host scores and 10 user scores, if available. To avoid missed data, keep this value consistent while paginating through scores. - default: 1000 - type: number - DateRange: - description: Defines the time period on which risk inputs will be filtered. - type: object - required: - - start - - end - properties: - start: - $ref: '#/components/schemas/KibanaDate' - end: - $ref: '#/components/schemas/KibanaDate' - KibanaDate: - type: string - oneOf: - - format: date - - format: date-time - - format: datemath - example: '2017-07-21T17:32:28Z' - IdentifierType: - type: string - enum: - - host - - user - RiskScore: - type: object - required: - - '@timestamp' - - id_field - - id_value - - calculated_level - - calculated_score - - calculated_score_norm - - category_1_score - - category_1_count - - inputs - properties: - '@timestamp': - type: string - format: 'date-time' - example: '2017-07-21T17:32:28Z' - description: The time at which the risk score was calculated. - id_field: - type: string - example: 'host.name' - description: The identifier field defining this risk score. Coupled with `id_value`, uniquely identifies the entity being scored. - id_value: - type: string - example: 'example.host' - description: The identifier value defining this risk score. Coupled with `id_field`, uniquely identifies the entity being scored. - calculated_level: - type: string - example: 'Critical' - description: Lexical description of the entity's risk. - calculated_score: - type: number - format: double - description: The raw numeric value of the given entity's risk score. - calculated_score_norm: - type: number - format: double - minimum: 0 - maximum: 100 - description: The normalized numeric value of the given entity's risk score. Useful for comparing with other entities. - category_1_score: - type: number - format: double - description: The contribution of Category 1 to the overall risk score (`calculated_score`). Category 1 contains Detection Engine Alerts. - category_1_count: - type: number - format: integer - description: The number of risk input documents that contributed to the Category 1 score (`category_1_score`). - inputs: - type: array - description: A list of the highest-risk documents contributing to this risk score. Useful for investigative purposes. - items: - $ref: '#/components/schemas/RiskScoreInput' - - RiskScoreInput: - description: A generic representation of a document contributing to a Risk Score. - type: object - properties: - id: - type: string - example: 91a93376a507e86cfbf282166275b89f9dbdb1f0be6c8103c6ff2909ca8e1a1c - description: The unique identifier (`_id`) of the original source document - index: - type: string - example: .internal.alerts-security.alerts-default-000001 - description: The unique index (`_index`) of the original source document - category: - type: string - example: category_1 - description: The risk category of the risk input document. - description: - type: string - example: 'Generated from Detection Engine Rule: Malware Prevention Alert' - description: A human-readable description of the risk input document. - risk_score: - type: number - format: double - minimum: 0 - maximum: 100 - description: The weighted risk score of the risk input document. - timestamp: - type: string - example: '2017-07-21T17:32:28Z' - description: The @timestamp of the risk input document. - RiskScoreWeight: - description: "Configuration used to tune risk scoring. Weights can be used to change the score contribution of risk inputs for hosts and users at both a global level and also for Risk Input categories (e.g. 'category_1')." - type: object - required: - - type - properties: - type: - type: string - value: - type: string - host: - type: number - format: double - minimum: 0 - maximum: 1 - user: - type: number - format: double - minimum: 0 - maximum: 1 - example: - type: 'risk_category' - value: 'category_1' - host: 0.8 - user: 0.4 - RiskScoreWeights: - description: 'A list of weights to be applied to the scoring calculation.' - type: array - items: - $ref: '#/components/schemas/RiskScoreWeight' - example: - - type: 'risk_category' - value: 'category_1' - host: 0.8 - user: 0.4 - - type: 'global_identifier' - host: 0.5 - user: 0.1 - RiskEngineStatus: - type: string - enum: - - 'NOT_INSTALLED' - - 'DISABLED' - - 'ENABLED' - RiskEngineInitStep: - type: object - required: - - type - - success - properties: - type: - type: string - success: - type: boolean - error: - type: string - RiskEnginePrivilegesResponse: - type: object - properties: - privileges: - type: object - properties: - elasticsearch: - type: object - properties: - cluster: - type: object - additionalProperties: - type: boolean - index: - type: object - additionalProperties: - type: object - additionalProperties: - type: boolean - has_all_required: - description: If true then the user has full access to the risk engine - type: boolean - RiskEngineSettingsResponse: - type: object - properties: - range: - $ref: '#/components/schemas/DateRange' diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_and_persist_risk_scores.mock.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_and_persist_risk_scores.mock.ts index 093e1b823511..27db9b98b528 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_and_persist_risk_scores.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_and_persist_risk_scores.mock.ts @@ -5,11 +5,11 @@ * 2.0. */ -import type { CalculateAndPersistScoresResponse } from '../types'; +import type { RiskScoresCalculationResponse } from '../../../../common/api/entity_analytics/risk_engine/calculation_route.gen'; const buildResponseMock = ( - overrides: Partial = {} -): CalculateAndPersistScoresResponse => ({ + overrides: Partial = {} +): RiskScoresCalculationResponse => ({ after_keys: { host: { 'host.name': 'hostname' }, }, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_and_persist_risk_scores.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_and_persist_risk_scores.ts index aa67d4abf78b..996beffbf62f 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_and_persist_risk_scores.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_and_persist_risk_scores.ts @@ -7,10 +7,11 @@ import type { ElasticsearchClient, Logger } from '@kbn/core/server'; +import type { RiskScoresCalculationResponse } from '../../../../common/api/entity_analytics/risk_engine/calculation_route.gen'; import type { RiskScoreDataClient } from './risk_score_data_client'; -import type { CalculateAndPersistScoresParams, CalculateAndPersistScoresResponse } from '../types'; import type { AssetCriticalityService } from '../asset_criticality/asset_criticality_service'; import { calculateRiskScores } from './calculate_risk_scores'; +import type { CalculateAndPersistScoresParams } from '../types'; export const calculateAndPersistRiskScores = async ( params: CalculateAndPersistScoresParams & { @@ -20,7 +21,7 @@ export const calculateAndPersistRiskScores = async ( spaceId: string; riskScoreDataClient: RiskScoreDataClient; } -): Promise => { +): Promise => { const { riskScoreDataClient, spaceId, returnScores, refresh, ...rest } = params; const writer = await riskScoreDataClient.getWriter({ diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.mock.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.mock.ts index 4789ecffe1f7..027c153f09fc 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.mock.ts @@ -5,13 +5,11 @@ * 2.0. */ -import { RiskCategories, RiskLevels } from '../../../../common/entity_analytics/risk_engine'; -import type { RiskScore } from '../../../../common/entity_analytics/risk_engine'; -import type { - CalculateRiskScoreAggregations, - CalculateScoresResponse, - RiskScoreBucket, -} from '../types'; +import { RiskCategories } from '../../../../common/entity_analytics/risk_engine'; +import type { CalculateRiskScoreAggregations, RiskScoreBucket } from '../types'; +import type { RiskScoresCalculationResponse } from '../../../../common/api/entity_analytics/risk_engine/calculation_route.gen'; +import type { EntityRiskScoreRecord } from '../../../../common/api/entity_analytics/common'; +import { EntityRiskLevelsEnum } from '../../../../common/api/entity_analytics/common'; const buildRiskScoreBucketMock = (overrides: Partial = {}): RiskScoreBucket => ({ key: { 'user.name': 'username' }, @@ -60,8 +58,8 @@ const buildAggregationResponseMock = ( }); const buildResponseMock = ( - overrides: Partial = {} -): CalculateScoresResponse => ({ + overrides: Partial = {} +): RiskScoresCalculationResponse => ({ after_keys: { host: { 'host.name': 'hostname' } }, scores: { host: [ @@ -71,7 +69,7 @@ const buildResponseMock = ( id_value: 'hostname', criticality_level: 'high_impact', criticality_modifier: 1.5, - calculated_level: RiskLevels.unknown, + calculated_level: EntityRiskLevelsEnum.Unknown, calculated_score: 20, calculated_score_norm: 30, category_1_score: 30, @@ -94,11 +92,13 @@ const buildResponseMock = ( ], user: [], }, + errors: [], + scores_written: 1, ...overrides, }); const buildResponseWithOneScoreMock = () => - buildResponseMock({ scores: { host: [{} as RiskScore] } }); + buildResponseMock({ scores: { host: [{} as EntityRiskScoreRecord] } }); export const calculateRiskScoresMock = { buildResponse: buildResponseMock, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts index c4ac061d259f..3c930ec07e66 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts @@ -18,11 +18,14 @@ import { ALERT_WORKFLOW_STATUS, EVENT_KIND, } from '@kbn/rule-registry-plugin/common/technical_rule_data_field_names'; +import type { RiskScoresPreviewResponse } from '../../../../common/api/entity_analytics/risk_engine/preview_route.gen'; +import type { + AfterKeys, + EntityRiskScoreRecord, + RiskScoreWeights, +} from '../../../../common/api/entity_analytics/common'; import { - type AfterKeys, type IdentifierType, - type RiskWeights, - type RiskScore, getRiskLevel, RiskCategories, } from '../../../../common/entity_analytics/risk_engine'; @@ -45,7 +48,6 @@ import { import type { CalculateRiskScoreAggregations, CalculateScoresParams, - CalculateScoresResponse, RiskScoreBucket, } from '../types'; import { @@ -67,7 +69,7 @@ const formatForResponse = ({ now: string; identifierField: string; includeNewFields: boolean; -}): RiskScore => { +}): EntityRiskScoreRecord => { const riskDetails = bucket.top_inputs.risk_details; const criticalityModifier = getCriticalityModifier(criticality?.criticality_level); @@ -173,7 +175,7 @@ const buildIdentifierTypeAggregation = ({ afterKeys: AfterKeys; identifierType: IdentifierType; pageSize: number; - weights?: RiskWeights; + weights?: RiskScoreWeights; alertSampleSizePerShard: number; }): AggregationsAggregationContainer => { const globalIdentifierTypeWeight = getGlobalWeightForIdentifierType({ identifierType, weights }); @@ -249,7 +251,7 @@ const processScores = async ({ identifierField: string; logger: Logger; now: string; -}): Promise => { +}): Promise => { if (buckets.length === 0) { return []; } @@ -302,7 +304,7 @@ export const calculateRiskScores = async ({ assetCriticalityService: AssetCriticalityService; esClient: ElasticsearchClient; logger: Logger; -} & CalculateScoresParams): Promise => +} & CalculateScoresParams): Promise => withSecuritySpan('calculateRiskScores', async () => { const now = new Date().toISOString(); const filter = [ diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/helpers.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/helpers.ts index f5df6ef1b80e..c39215cb0cc2 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/helpers.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/helpers.ts @@ -5,12 +5,9 @@ * 2.0. */ -import type { - AfterKey, - AfterKeys, - IdentifierType, -} from '../../../../common/entity_analytics/risk_engine'; -import type { CalculateAndPersistScoresResponse } from '../types'; +import type { RiskScoresCalculationResponse } from '../../../../common/api/entity_analytics/risk_engine/calculation_route.gen'; +import type { AfterKeys, EntityAfterKey } from '../../../../common/api/entity_analytics/common'; +import type { IdentifierType } from '../../../../common/entity_analytics/risk_engine'; export const getFieldForIdentifier = (identifierType: IdentifierType): string => identifierType === 'host' ? 'host.name' : 'user.name'; @@ -21,10 +18,8 @@ export const getAfterKeyForIdentifierType = ({ }: { afterKeys: AfterKeys; identifierType: IdentifierType; -}): AfterKey | undefined => afterKeys[identifierType]; +}): EntityAfterKey | undefined => afterKeys[identifierType]; -export const isRiskScoreCalculationComplete = ( - result: CalculateAndPersistScoresResponse -): boolean => +export const isRiskScoreCalculationComplete = (result: RiskScoresCalculationResponse): boolean => Object.keys(result.after_keys.host ?? {}).length === 0 && Object.keys(result.after_keys.user ?? {}).length === 0; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_engine_data_writer.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_engine_data_writer.ts index e140090ea55e..e24f09e7b2cc 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_engine_data_writer.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_engine_data_writer.ts @@ -7,7 +7,8 @@ import type { BulkOperationContainer } from '@elastic/elasticsearch/lib/api/types'; import type { Logger, ElasticsearchClient } from '@kbn/core/server'; -import type { IdentifierType, RiskScore } from '../../../../common/entity_analytics/risk_engine'; +import type { EntityRiskScoreRecord } from '../../../../common/api/entity_analytics/common'; +import type { IdentifierType } from '../../../../common/entity_analytics/risk_engine'; interface WriterBulkResponse { errors: string[]; @@ -16,8 +17,8 @@ interface WriterBulkResponse { } interface BulkParams { - host?: RiskScore[]; - user?: RiskScore[]; + host?: EntityRiskScoreRecord[]; + user?: EntityRiskScoreRecord[]; refresh?: 'wait_for'; } @@ -83,7 +84,7 @@ export class RiskEngineDataWriter implements RiskEngineDataWriter { return hostBody.concat(userBody) as BulkOperationContainer[]; }; - private scoreToEcs = (score: RiskScore, identifierType: IdentifierType): unknown => { + private scoreToEcs = (score: EntityRiskScoreRecord, identifierType: IdentifierType): unknown => { const { '@timestamp': _, ...rest } = score; return { '@timestamp': score['@timestamp'], diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_service.mock.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_service.mock.ts index a7ca5c6ce27d..2423a87559b1 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_service.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_service.mock.ts @@ -6,14 +6,16 @@ */ import type { RiskScoreService } from './risk_score_service'; -import type { RiskScore } from '../../../../common/entity_analytics/risk_engine'; -import { RiskLevels } from '../../../../common/entity_analytics/risk_engine'; +import type { EntityRiskScoreRecord } from '../../../../common/api/entity_analytics/common'; +import { EntityRiskLevelsEnum } from '../../../../common/api/entity_analytics/common'; -const createRiskScoreMock = (overrides: Partial = {}): RiskScore => ({ +const createRiskScoreMock = ( + overrides: Partial = {} +): EntityRiskScoreRecord => ({ '@timestamp': '2023-02-15T00:15:19.231Z', id_field: 'host.name', id_value: 'hostname', - calculated_level: RiskLevels.high, + calculated_level: EntityRiskLevelsEnum.High, calculated_score: 149, calculated_score_norm: 85.332, category_1_score: 85, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_service.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_service.ts index b326f50f767a..cb89d61d8720 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_service.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_service.ts @@ -6,11 +6,11 @@ */ import type { ElasticsearchClient, Logger } from '@kbn/core/server'; +import type { RiskScoresCalculationResponse } from '../../../../common/api/entity_analytics/risk_engine/calculation_route.gen'; +import type { RiskScoresPreviewResponse } from '../../../../common/api/entity_analytics/risk_engine/preview_route.gen'; import type { CalculateAndPersistScoresParams, - CalculateAndPersistScoresResponse, CalculateScoresParams, - CalculateScoresResponse, EntityAnalyticsConfig, RiskEngineConfiguration, } from '../types'; @@ -26,10 +26,10 @@ export type RiskEngineConfigurationWithDefaults = RiskEngineConfiguration & { alertSampleSizePerShard: number; }; export interface RiskScoreService { - calculateScores: (params: CalculateScoresParams) => Promise; + calculateScores: (params: CalculateScoresParams) => Promise; calculateAndPersistScores: ( params: CalculateAndPersistScoresParams - ) => Promise; + ) => Promise; getConfigurationWithDefaults: ( entityAnalyticsConfig: EntityAnalyticsConfig ) => Promise; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.ts index de1754ba3de2..d0c7486324e3 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.ts @@ -7,17 +7,17 @@ import { keyBy, merge } from 'lodash'; import type { - GlobalRiskWeight, - IdentifierType, - RiskCategoryRiskWeight, - RiskWeight, - RiskWeights, -} from '../../../../common/entity_analytics/risk_engine'; + RiskScoreWeight, + RiskScoreWeightCategory, + RiskScoreWeightGlobal, + RiskScoreWeights, +} from '../../../../common/api/entity_analytics/common'; +import type { IdentifierType } from '../../../../common/entity_analytics/risk_engine'; import { RiskCategories, RiskWeightTypes } from '../../../../common/entity_analytics/risk_engine'; const RISK_CATEGORIES = Object.values(RiskCategories); -const DEFAULT_CATEGORY_WEIGHTS: RiskWeights = RISK_CATEGORIES.map((category) => ({ +const DEFAULT_CATEGORY_WEIGHTS: RiskScoreWeights = RISK_CATEGORIES.map((category) => ({ type: RiskWeightTypes.riskCategory, value: category, host: 1, @@ -30,9 +30,9 @@ const DEFAULT_CATEGORY_WEIGHTS: RiskWeights = RISK_CATEGORIES.map((category) => const convertCategoryToEventKindValue = (category?: string): string | undefined => category === 'category_1' ? 'signal' : category; -const isGlobalIdentifierTypeWeight = (weight: RiskWeight): weight is GlobalRiskWeight => +const isGlobalIdentifierTypeWeight = (weight: RiskScoreWeight): weight is RiskScoreWeightGlobal => weight.type === RiskWeightTypes.global; -const isRiskCategoryWeight = (weight: RiskWeight): weight is RiskCategoryRiskWeight => +const isRiskCategoryWeight = (weight: RiskScoreWeight): weight is RiskScoreWeightCategory => weight.type === RiskWeightTypes.riskCategory; export const getGlobalWeightForIdentifierType = ({ @@ -40,15 +40,18 @@ export const getGlobalWeightForIdentifierType = ({ weights, }: { identifierType: IdentifierType; - weights?: RiskWeights; + weights?: RiskScoreWeights; }): number | undefined => { return weights?.find(isGlobalIdentifierTypeWeight)?.[identifierType]; }; -const getRiskCategoryWeights = (weights?: RiskWeights): RiskCategoryRiskWeight[] => +const getRiskCategoryWeights = (weights?: RiskScoreWeights): RiskScoreWeightCategory[] => weights?.filter(isRiskCategoryWeight) ?? []; -const getWeightForIdentifierType = (weight: RiskWeight, identifierType: IdentifierType): number => { +const getWeightForIdentifierType = ( + weight: RiskScoreWeight, + identifierType: IdentifierType +): number => { const configuredWeight = weight[identifierType]; return typeof configuredWeight === 'number' ? configuredWeight : 1; }; @@ -61,7 +64,7 @@ export const buildCategoryCountDeclarations = (): string => { return RISK_CATEGORIES.map((riskCategory) => `results['${riskCategory}_count'] = 0;`).join(''); }; -export const buildCategoryWeights = (userWeights?: RiskWeights): RiskCategoryRiskWeight[] => { +export const buildCategoryWeights = (userWeights?: RiskScoreWeights): RiskScoreWeightCategory[] => { const categoryWeights = getRiskCategoryWeights(userWeights); return Object.values( @@ -82,7 +85,7 @@ export const buildWeightingOfScoreByCategory = ({ userWeights, identifierType, }: { - userWeights?: RiskWeights; + userWeights?: RiskScoreWeights; identifierType: IdentifierType; }): string => { const otherClause = `weighted_score = score;`; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.test.ts index ad59a7306918..9ef1cc8bc210 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.test.ts @@ -100,27 +100,21 @@ describe('risk score calculation route', () => { const request = buildRequest({ data_view_id: undefined }); const result = await server.validate(request); - expect(result.badRequest).toHaveBeenCalledWith( - 'Invalid value "undefined" supplied to "data_view_id"' - ); + expect(result.badRequest).toHaveBeenCalledWith('data_view_id: Required'); }); it('requires a parameter for the date range', async () => { const request = buildRequest({ range: undefined }); const result = await server.validate(request); - expect(result.badRequest).toHaveBeenCalledWith( - 'Invalid value "undefined" supplied to "range"' - ); + expect(result.badRequest).toHaveBeenCalledWith('range: Required'); }); it('requires a parameter for the identifier type', async () => { const request = buildRequest({ identifier_type: undefined }); const result = await server.validate(request); - expect(result.badRequest).toHaveBeenCalledWith( - 'Invalid value "undefined" supplied to "identifier_type"' - ); + expect(result.badRequest).toHaveBeenCalledWith('identifier_type: Required'); }); }); @@ -143,9 +137,7 @@ describe('risk score calculation route', () => { const request = buildRequest({ range: 'bad range' }); const result = await server.validate(request); - expect(result.badRequest).toHaveBeenCalledWith( - 'Invalid value "bad range" supplied to "range"' - ); + expect(result.badRequest).toHaveBeenCalledWith('range: Expected object, received string'); }); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.ts index 210d792ded72..be4875d7dee0 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.ts @@ -8,13 +8,13 @@ import type { Logger } from '@kbn/core/server'; import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; import { transformError } from '@kbn/securitysolution-es-utils'; +import { RiskScoresCalculationRequest } from '../../../../../common/api/entity_analytics/risk_engine/calculation_route.gen'; import { APP_ID, DEFAULT_RISK_SCORE_PAGE_SIZE, RISK_SCORE_CALCULATION_URL, } from '../../../../../common/constants'; -import { riskScoreCalculationRequestSchema } from '../../../../../common/entity_analytics/risk_engine/risk_score_calculation/request_schema'; -import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; +import { buildRouteValidationWithZod } from '../../../../utils/build_validation/route_validation'; import { getRiskInputsIndex } from '../get_risk_inputs_index'; import type { EntityAnalyticsRoutesDeps } from '../../types'; import { RiskScoreAuditActions } from '../audit'; @@ -36,7 +36,7 @@ export const riskScoreCalculationRoute = ( .addVersion( { version: '1', - validate: { request: { body: buildRouteValidation(riskScoreCalculationRequestSchema) } }, + validate: { request: { body: buildRouteValidationWithZod(RiskScoresCalculationRequest) } }, }, async (context, request, response) => { const securityContext = await context.securitySolution; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/entity_calculation.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/entity_calculation.ts index 98b4149f7023..502e296db364 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/entity_calculation.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/entity_calculation.ts @@ -10,12 +10,13 @@ import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; import { transformError } from '@kbn/securitysolution-es-utils'; import { isEmpty } from 'lodash/fp'; +import type { RiskScoresCalculationResponse } from '../../../../../common/api/entity_analytics/risk_engine/calculation_route.gen'; +import type { AfterKeys } from '../../../../../common/api/entity_analytics/common'; import { RiskScoresEntityCalculationRequest } from '../../../../../common/api/entity_analytics/risk_engine/entity_calculation_route.gen'; import { APP_ID, RISK_SCORE_ENTITY_CALCULATION_URL } from '../../../../../common/constants'; -import type { AfterKeys } from '../../../../../common/entity_analytics/risk_engine'; import { buildRouteValidationWithZod } from '../../../../utils/build_validation/route_validation'; import { getRiskInputsIndex } from '../get_risk_inputs_index'; -import type { CalculateAndPersistScoresResponse, EntityAnalyticsRoutesDeps } from '../../types'; +import type { EntityAnalyticsRoutesDeps } from '../../types'; import { RiskScoreAuditActions } from '../audit'; import { AUDIT_CATEGORY, AUDIT_OUTCOME, AUDIT_TYPE } from '../../audit'; import { convertRangeToISO } from '../tasks/helpers'; @@ -115,7 +116,7 @@ export const riskScoreEntityCalculationRoute = ( const filter = isEmpty(userFilter) ? [identifierFilter] : [userFilter, identifierFilter]; - const result: CalculateAndPersistScoresResponse = + const result: RiskScoresCalculationResponse = await riskScoreService.calculateAndPersistScores({ pageSize, identifierType, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts index 9a525a5bae0d..a35f4978ebf2 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts @@ -67,9 +67,7 @@ describe('POST risk_engine/preview route', () => { const request = buildRequest({ data_view_id: undefined }); const result = await server.validate(request); - expect(result.badRequest).toHaveBeenCalledWith( - 'Invalid value "undefined" supplied to "data_view_id"' - ); + expect(result.badRequest).toHaveBeenCalledWith('data_view_id: Required'); }); it('respects the provided dataview', async () => { @@ -127,7 +125,7 @@ describe('POST risk_engine/preview route', () => { const result = await server.validate(request); expect(result.badRequest).toHaveBeenCalledWith( - expect.stringContaining('Invalid value "undefined" supplied to "range,start"') + expect.stringContaining('range.start: Required') ); }); }); @@ -211,7 +209,7 @@ describe('POST risk_engine/preview route', () => { const result = await server.validate(request); expect(result.badRequest).toHaveBeenCalledWith( - expect.stringContaining('Invalid value "1.1" supplied to "weights,host"') + expect.stringContaining('weights.0.host: Number must be less than or equal to 1') ); }); @@ -220,14 +218,16 @@ describe('POST risk_engine/preview route', () => { weights: [ { type: 'something new', - host: 1.1, + value: RiskCategories.category_1, + host: 0.1, + user: 0.2, }, ], }); const result = await server.validate(request); expect(result.badRequest).toHaveBeenCalledWith( - 'Invalid value "{"type":"something new","host":1.1}" supplied to "weights"' + expect.stringContaining('weights.0.type: Invalid literal value') ); }); }); @@ -245,7 +245,7 @@ describe('POST risk_engine/preview route', () => { ); }); - it('rejects an invalid after_key', async () => { + it('remove invalid after_key property', async () => { const request = buildRequest({ after_keys: { bad: 'key', @@ -253,7 +253,8 @@ describe('POST risk_engine/preview route', () => { }); const result = await server.validate(request); - expect(result.badRequest).toHaveBeenCalledWith('invalid keys "bad"'); + + expect(result.ok).toHaveBeenCalledWith(expect.objectContaining({ after_keys: {} })); }); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.ts index b592f3a8a48c..0979b5900737 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.ts @@ -9,13 +9,13 @@ import type { Logger } from '@kbn/core/server'; import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; import { transformError } from '@kbn/securitysolution-es-utils'; +import { RiskScoresPreviewRequest } from '../../../../../common/api/entity_analytics/risk_engine/preview_route.gen'; import { APP_ID, DEFAULT_RISK_SCORE_PAGE_SIZE, RISK_SCORE_PREVIEW_URL, } from '../../../../../common/constants'; -import { riskScorePreviewRequestSchema } from '../../../../../common/entity_analytics/risk_engine/risk_score_preview/request_schema'; -import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; +import { buildRouteValidationWithZod } from '../../../../utils/build_validation/route_validation'; import { getRiskInputsIndex } from '../get_risk_inputs_index'; import type { EntityAnalyticsRoutesDeps } from '../../types'; import { RiskScoreAuditActions } from '../audit'; @@ -37,7 +37,9 @@ export const riskScorePreviewRoute = ( .addVersion( { version: '1', - validate: { request: { body: buildRouteValidation(riskScorePreviewRequestSchema) } }, + validate: { + request: { body: buildRouteValidationWithZod(RiskScoresPreviewRequest) }, + }, }, async (context, request, response) => { const siemResponse = buildSiemResponse(response); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.ts index b3d25855ba42..1b2b44dae6a9 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.ts @@ -15,8 +15,8 @@ import type { } from '@kbn/task-manager-plugin/server'; import type { AnalyticsServiceSetup } from '@kbn/core-analytics-server'; import type { AuditLogger } from '@kbn/security-plugin-types-server'; +import type { AfterKeys } from '../../../../../common/api/entity_analytics/common'; import { - type AfterKeys, type IdentifierType, RiskScoreEntity, } from '../../../../../common/entity_analytics/risk_engine'; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/types.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/types.ts index a71912d2dffa..dfc4e45187e0 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/types.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/types.ts @@ -5,17 +5,14 @@ * 2.0. */ -import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; import type { Logger, StartServicesAccessor } from '@kbn/core/server'; +import type { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; import type { - AfterKey, AfterKeys, - IdentifierType, - RiskWeights, - Range, - RiskEngineStatus, - RiskScore, -} from '../../../common/entity_analytics/risk_engine'; + EntityAfterKey, + RiskScoreWeights, +} from '../../../common/api/entity_analytics/common'; +import type { IdentifierType, Range } from '../../../common/entity_analytics/risk_engine'; import type { ConfigType } from '../../config'; import type { StartPlugins } from '../../plugin'; import type { SecuritySolutionPluginRouter } from '../../types'; @@ -27,103 +24,14 @@ export interface EntityAnalyticsRoutesDeps { config: ConfigType; getStartServices: StartServicesAccessor; } -export interface CalculateScoresParams { - afterKeys: AfterKeys; - debug?: boolean; - index: string; - filter?: unknown; - identifierType?: IdentifierType; - pageSize: number; - range: { start: string; end: string }; - runtimeMappings: MappingRuntimeFields; - weights?: RiskWeights; - alertSampleSizePerShard?: number; -} - -export interface CalculateAndPersistScoresParams { - afterKeys: AfterKeys; - debug?: boolean; - index: string; - filter?: unknown; - identifierType: IdentifierType; - pageSize: number; - range: Range; - runtimeMappings: MappingRuntimeFields; - weights?: RiskWeights; - alertSampleSizePerShard?: number; - returnScores?: boolean; - refresh?: 'wait_for'; -} - -export interface CalculateAndPersistScoresResponse { - after_keys: AfterKeys; - errors: string[]; - scores_written: number; - scores?: { - host?: RiskScore[]; - user?: RiskScore[]; - }; -} - -export interface CalculateScoresResponse { - debug?: { - request: unknown; - response: unknown; - }; - after_keys: AfterKeys; - scores: { - host?: RiskScore[]; - user?: RiskScore[]; - }; -} - -export interface GetRiskEngineStatusResponse { - legacy_risk_engine_status: RiskEngineStatus; - risk_engine_status: RiskEngineStatus; - is_max_amount_of_risk_engines_reached: boolean; -} - -export interface InitRiskEngineResultResponse { - risk_engine_enabled: boolean; - risk_engine_resources_installed: boolean; - risk_engine_configuration_created: boolean; - legacy_risk_engine_disabled: boolean; - errors: string[]; -} - -export interface InitRiskEngineResponse { - result: InitRiskEngineResultResponse; -} - -export interface InitRiskEngineError { - body: { - message: string; - full_error: InitRiskEngineResultResponse | undefined; - }; -} - -export interface EnableDisableRiskEngineErrorResponse { - body: { - message: string; - full_error: string; - }; -} - -export interface EnableRiskEngineResponse { - success: boolean; -} - -export interface DisableRiskEngineResponse { - success: boolean; -} export interface CalculateRiskScoreAggregations { user?: { - after_key: AfterKey; + after_key: EntityAfterKey; buckets: RiskScoreBucket[]; }; host?: { - after_key: AfterKey; + after_key: EntityAfterKey; buckets: RiskScoreBucket[]; }; } @@ -165,3 +73,31 @@ export interface RiskEngineConfiguration { range: Range; alertSampleSizePerShard?: number; } + +export interface CalculateScoresParams { + afterKeys: AfterKeys; + debug?: boolean; + index: string; + filter?: unknown; + identifierType?: IdentifierType; + pageSize: number; + range: { start: string; end: string }; + runtimeMappings: MappingRuntimeFields; + weights?: RiskScoreWeights; + alertSampleSizePerShard?: number; +} + +export interface CalculateAndPersistScoresParams { + afterKeys: AfterKeys; + debug?: boolean; + index: string; + filter?: unknown; + identifierType: IdentifierType; + pageSize: number; + range: Range; + runtimeMappings: MappingRuntimeFields; + weights?: RiskScoreWeights; + alertSampleSizePerShard?: number; + returnScores?: boolean; + refresh?: 'wait_for'; +} diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/check_and_format_privileges.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/check_and_format_privileges.ts index 0c9ac9036fad..713405b11d5e 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/check_and_format_privileges.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/check_and_format_privileges.ts @@ -11,8 +11,8 @@ import type { CheckPrivilegesResponse, SecurityPluginStart, } from '@kbn/security-plugin/server'; +import type { EntityAnalyticsPrivileges } from '../../../../common/api/entity_analytics'; import { ASSET_CRITICALITY_INDEX_PATTERN } from '../../../../common/entity_analytics/asset_criticality/constants'; -import type { EntityAnalyticsPrivileges } from '../../../../common/api/entity_analytics/common'; const groupPrivilegesByName = ( privileges: Array<{ privilege: PrivilegeName; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/all/index.test.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/all/index.test.ts index b4be7a7cef71..81f468d5dda0 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/all/index.test.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/risk_score/all/index.test.ts @@ -42,7 +42,7 @@ export const mockSearchStrategyResponse: IEsSearchResponse = { name: 'testUsername', risk: { rule_risks: [], - calculated_level: RiskSeverity.high, + calculated_level: RiskSeverity.High, calculated_score_norm: 75, multipliers: [], id_field: '', diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_calculation.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_calculation.ts index 653c0d448382..1dd6b0f9e576 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_calculation.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_calculation.ts @@ -9,8 +9,8 @@ import expect from '@kbn/expect'; import { X_ELASTIC_INTERNAL_ORIGIN_REQUEST } from '@kbn/core-http-common'; import { RISK_SCORE_CALCULATION_URL } from '@kbn/security-solution-plugin/common/constants'; -import type { RiskScore } from '@kbn/security-solution-plugin/common/entity_analytics/risk_engine'; import { v4 as uuidv4 } from 'uuid'; +import { EntityRiskScoreRecord } from '@kbn/security-solution-plugin/common/api/entity_analytics/common'; import { dataGeneratorFactory } from '../../../detections_response/utils'; import { deleteAllAlerts, deleteAllRules } from '../../../../../common/utils/security_solution'; import { @@ -46,7 +46,7 @@ export default ({ getService }: FtrProviderContext): void => { body, }: { body: object; - }): Promise<{ scores: RiskScore[] }> => { + }): Promise<{ scores: EntityRiskScoreRecord[] }> => { const { body: result } = await supertest .post(RISK_SCORE_CALCULATION_URL) .set('kbn-xsrf', 'true') diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_entity_calculation.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_entity_calculation.ts index 2e1b3a440657..e3ddfbbda4ef 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_entity_calculation.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_entity_calculation.ts @@ -9,8 +9,8 @@ import expect from '@kbn/expect'; import { X_ELASTIC_INTERNAL_ORIGIN_REQUEST } from '@kbn/core-http-common'; import { RISK_SCORE_ENTITY_CALCULATION_URL } from '@kbn/security-solution-plugin/common/constants'; -import type { RiskScore } from '@kbn/security-solution-plugin/common/entity_analytics/risk_engine'; import { v4 as uuidv4 } from 'uuid'; +import { EntityRiskScoreRecord } from '@kbn/security-solution-plugin/common/api/entity_analytics/common'; import { dataGeneratorFactory } from '../../../detections_response/utils'; import { deleteAllAlerts, deleteAllRules } from '../../../../../common/utils/security_solution'; import { @@ -46,7 +46,7 @@ export default ({ getService }: FtrProviderContext): void => { body, }: { body: object; - }): Promise<{ score: RiskScore; success: boolean }> => { + }): Promise<{ score: EntityRiskScoreRecord; success: boolean }> => { const { body: result } = await supertest .post(RISK_SCORE_ENTITY_CALCULATION_URL) .set('kbn-xsrf', 'true') diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_preview.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_preview.ts index a9c2c9eb37b1..98ea7b172583 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_preview.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_score_preview.ts @@ -8,9 +8,9 @@ import expect from '@kbn/expect'; import { ALERT_RISK_SCORE } from '@kbn/rule-data-utils'; import { RISK_SCORE_PREVIEW_URL } from '@kbn/security-solution-plugin/common/constants'; -import type { RiskScore } from '@kbn/security-solution-plugin/common/entity_analytics/risk_engine'; import { v4 as uuidv4 } from 'uuid'; import { X_ELASTIC_INTERNAL_ORIGIN_REQUEST } from '@kbn/core-http-common'; +import { EntityRiskScoreRecord } from '@kbn/security-solution-plugin/common/api/entity_analytics/common'; import { dataGeneratorFactory } from '../../../detections_response/utils'; import { createAlertsIndex, @@ -42,7 +42,9 @@ export default ({ getService }: FtrProviderContext): void => { body, }: { body: object; - }): Promise<{ scores: { host?: RiskScore[]; user?: RiskScore[] } }> => { + }): Promise<{ + scores: { host?: EntityRiskScoreRecord[]; user?: EntityRiskScoreRecord[] }; + }> => { const defaultBody = { data_view_id: '.alerts-security.alerts-default' }; const { body: result } = await supertest .post(RISK_SCORE_PREVIEW_URL) diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/risk_engine.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/risk_engine.ts index 82f33d6637d6..376aaeb735bb 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/risk_engine.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/utils/risk_engine.ts @@ -13,10 +13,7 @@ import { v4 as uuidv4 } from 'uuid'; import SuperTest from 'supertest'; import type { Client } from '@elastic/elasticsearch'; import type { ToolingLog } from '@kbn/tooling-log'; -import type { - EcsRiskScore, - RiskScore, -} from '@kbn/security-solution-plugin/common/entity_analytics/risk_engine'; +import type { EcsRiskScore } from '@kbn/security-solution-plugin/common/entity_analytics/risk_engine'; import { riskEngineConfigurationTypeName } from '@kbn/security-solution-plugin/server/lib/entity_analytics/risk_engine/saved_object'; import type { KbnClient } from '@kbn/test'; import { @@ -27,6 +24,7 @@ import { RISK_ENGINE_PRIVILEGES_URL, } from '@kbn/security-solution-plugin/common/constants'; import { MappingTypeMapping } from '@elastic/elasticsearch/lib/api/types'; +import { EntityRiskScoreRecord } from '@kbn/security-solution-plugin/common/api/entity_analytics/common'; import { createRule, waitForAlertsToBePresent, @@ -37,7 +35,7 @@ import { routeWithNamespace, } from '../../../../common/utils/security_solution'; -const sanitizeScore = (score: Partial): Partial => { +const sanitizeScore = (score: Partial): Partial => { const { '@timestamp': timestamp, inputs, @@ -49,10 +47,13 @@ const sanitizeScore = (score: Partial): Partial => { return rest; }; -export const sanitizeScores = (scores: Array>): Array> => - scores.map(sanitizeScore); +export const sanitizeScores = ( + scores: Array> +): Array> => scores.map(sanitizeScore); -export const normalizeScores = (scores: Array>): Array> => +export const normalizeScores = ( + scores: Array> +): Array> => scores.map((score) => sanitizeScore(score.host?.risk ?? score.user?.risk ?? {})); export const buildDocument = (body: object, id?: string) => {