From bb7ad1fe688da3be0ddfc00c4df61e44cde05af4 Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Fri, 30 Aug 2024 19:26:03 +0530 Subject: [PATCH 01/25] Adding initial changes for new API endpoint to cleanup the risk engine installation and data --- .../entity_analytics/risk_engine/constants.ts | 19 ++- .../public/entity_analytics/api/api.ts | 148 +++++++++++------- .../risk_engine_data_client.mock.ts | 7 +- .../risk_engine/routes/delete.test.ts | 101 ++++++++++++ .../risk_engine/routes/delete.ts | 89 +++++++++++ .../routes/register_risk_engine_routes.ts | 16 +- .../entity_analytics/utils/risk_engine.ts | 10 ++ 7 files changed, 316 insertions(+), 74 deletions(-) create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts index c8214c0daef1e..bcf141359ddca 100644 --- a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts +++ b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts @@ -4,27 +4,30 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { INTERNAL_RISK_SCORE_URL } from '../risk_score/constants'; +import { INTERNAL_RISK_SCORE_URL } from "../risk_score/constants"; export const RISK_ENGINE_URL = `${INTERNAL_RISK_SCORE_URL}/engine` as const; export const RISK_ENGINE_STATUS_URL = `${RISK_ENGINE_URL}/status` as const; export const RISK_ENGINE_INIT_URL = `${RISK_ENGINE_URL}/init` as const; export const RISK_ENGINE_ENABLE_URL = `${RISK_ENGINE_URL}/enable` as const; export const RISK_ENGINE_DISABLE_URL = `${RISK_ENGINE_URL}/disable` as const; -export const RISK_ENGINE_PRIVILEGES_URL = `${RISK_ENGINE_URL}/privileges` as const; +export const RISK_ENGINE_PRIVILEGES_URL = + `${RISK_ENGINE_URL}/privileges` as const; export const RISK_ENGINE_SETTINGS_URL = `${RISK_ENGINE_URL}/settings` as const; +export const RISK_ENGINE_INSTALLATION_AND_DATA_CLEANUP_URL = + `/api/risk_score/engine/dangerously_delete_data` as const; export const MAX_SPACES_COUNT = 1; -type ClusterPrivilege = 'manage_index_templates' | 'manage_transform'; +type ClusterPrivilege = "manage_index_templates" | "manage_transform"; export const RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES = [ - 'manage_index_templates', - 'manage_transform', + "manage_index_templates", + "manage_transform", ] as ClusterPrivilege[]; -export const RISK_SCORE_INDEX_PATTERN = 'risk-score.risk-score-*'; +export const RISK_SCORE_INDEX_PATTERN = "risk-score.risk-score-*"; -export type RiskEngineIndexPrivilege = 'read' | 'write'; +export type RiskEngineIndexPrivilege = "read" | "write"; export const RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES = Object.freeze({ - [RISK_SCORE_INDEX_PATTERN]: ['read', 'write'] as RiskEngineIndexPrivilege[], + [RISK_SCORE_INDEX_PATTERN]: ["read", "write"] as RiskEngineIndexPrivilege[], }); 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 9351e34ab4b5b..7dcfd3a42f895 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 @@ -5,25 +5,25 @@ * 2.0. */ -import { useMemo } from 'react'; -import type { UploadAssetCriticalityRecordsResponse } from '../../../common/api/entity_analytics/asset_criticality/upload_asset_criticality_csv.gen'; -import type { DisableRiskEngineResponse } 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 { InitRiskEngineResponse } from '../../../common/api/entity_analytics/risk_engine/engine_init_route.gen'; -import type { EnableRiskEngineResponse } from '../../../common/api/entity_analytics/risk_engine/engine_enable_route.gen'; +import { useMemo } from "react"; +import type { UploadAssetCriticalityRecordsResponse } from "../../../common/api/entity_analytics/asset_criticality/upload_asset_criticality_csv.gen"; +import type { DisableRiskEngineResponse } 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 { InitRiskEngineResponse } from "../../../common/api/entity_analytics/risk_engine/engine_init_route.gen"; +import type { EnableRiskEngineResponse } 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'; +} 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'; +} from "../../../common/api/entity_analytics/risk_engine/entity_calculation_route.gen"; import type { AssetCriticalityRecord, EntityAnalyticsPrivileges, -} from '../../../common/api/entity_analytics'; -import type { RiskScoreEntity } from '../../../common/search_strategy'; +} from "../../../common/api/entity_analytics"; +import type { RiskScoreEntity } from "../../../common/search_strategy"; import { RISK_ENGINE_STATUS_URL, RISK_SCORE_PREVIEW_URL, @@ -38,10 +38,11 @@ import { ASSET_CRITICALITY_PUBLIC_CSV_UPLOAD_URL, RISK_SCORE_ENTITY_CALCULATION_URL, API_VERSIONS, -} from '../../../common/constants'; -import type { SnakeToCamelCase } from '../common/utils'; -import { useKibana } from '../../common/lib/kibana/kibana_react'; -import type { ReadRiskEngineSettingsResponse } from '../../../common/api/entity_analytics/risk_engine'; + RISK_ENGINE_INSTALLATION_AND_DATA_CLEANUP_URL, +} from "../../../common/constants"; +import type { SnakeToCamelCase } from "../common/utils"; +import { useKibana } from "../../common/lib/kibana/kibana_react"; +import type { ReadRiskEngineSettingsResponse } from "../../../common/api/entity_analytics/risk_engine"; export interface DeleteAssetCriticalityResponse { deleted: true; @@ -61,8 +62,8 @@ export const useEntityAnalyticsRoutes = () => { params: RiskScoresPreviewRequest; }) => http.fetch(RISK_SCORE_PREVIEW_URL, { - version: '1', - method: 'POST', + version: "1", + method: "POST", body: JSON.stringify(params), signal, }); @@ -72,8 +73,8 @@ export const useEntityAnalyticsRoutes = () => { */ const fetchRiskEngineStatus = ({ signal }: { signal?: AbortSignal }) => http.fetch(RISK_ENGINE_STATUS_URL, { - version: '1', - method: 'GET', + version: "1", + method: "GET", signal, }); @@ -82,8 +83,8 @@ export const useEntityAnalyticsRoutes = () => { */ const initRiskEngine = () => http.fetch(RISK_ENGINE_INIT_URL, { - version: '1', - method: 'POST', + version: "1", + method: "POST", }); /** @@ -91,8 +92,8 @@ export const useEntityAnalyticsRoutes = () => { */ const enableRiskEngine = () => http.fetch(RISK_ENGINE_ENABLE_URL, { - version: '1', - method: 'POST', + version: "1", + method: "POST", }); /** @@ -100,19 +101,24 @@ export const useEntityAnalyticsRoutes = () => { */ const disableRiskEngine = () => http.fetch(RISK_ENGINE_DISABLE_URL, { - version: '1', - method: 'POST', + version: "1", + method: "POST", }); /** * Calculate and stores risk score for an entity */ - const calculateEntityRiskScore = (params: RiskScoresEntityCalculationRequest) => { - return http.fetch(RISK_SCORE_ENTITY_CALCULATION_URL, { - version: '1', - method: 'POST', - body: JSON.stringify(params), - }); + const calculateEntityRiskScore = ( + params: RiskScoresEntityCalculationRequest + ) => { + return http.fetch( + RISK_SCORE_ENTITY_CALCULATION_URL, + { + version: "1", + method: "POST", + body: JSON.stringify(params), + } + ); }; /** @@ -120,30 +126,36 @@ export const useEntityAnalyticsRoutes = () => { */ const fetchRiskEnginePrivileges = () => http.fetch(RISK_ENGINE_PRIVILEGES_URL, { - version: '1', - method: 'GET', + version: "1", + method: "GET", }); /** * Get asset criticality privileges */ const fetchAssetCriticalityPrivileges = () => - http.fetch(ASSET_CRITICALITY_INTERNAL_PRIVILEGES_URL, { - version: '1', - method: 'GET', - }); + http.fetch( + ASSET_CRITICALITY_INTERNAL_PRIVILEGES_URL, + { + version: "1", + method: "GET", + } + ); /** * Create asset criticality */ const createAssetCriticality = async ( - params: Pick & { - refresh?: 'wait_for'; + params: Pick< + AssetCriticality, + "idField" | "idValue" | "criticalityLevel" + > & { + refresh?: "wait_for"; } ): Promise => http.fetch(ASSET_CRITICALITY_PUBLIC_URL, { version: API_VERSIONS.public.v1, - method: 'POST', + method: "POST", body: JSON.stringify({ id_value: params.idValue, id_field: params.idField, @@ -153,12 +165,18 @@ export const useEntityAnalyticsRoutes = () => { }); const deleteAssetCriticality = async ( - params: Pick & { refresh?: 'wait_for' } + params: Pick & { + refresh?: "wait_for"; + } ): Promise<{ deleted: true }> => { await http.fetch(ASSET_CRITICALITY_PUBLIC_URL, { version: API_VERSIONS.public.v1, - method: 'DELETE', - query: { id_value: params.idValue, id_field: params.idField, refresh: params.refresh }, + method: "DELETE", + query: { + id_value: params.idValue, + id_field: params.idField, + refresh: params.refresh, + }, }); // spoof a response to allow us to better distnguish a delete from a create in use_asset_criticality.ts @@ -169,11 +187,11 @@ export const useEntityAnalyticsRoutes = () => { * Get asset criticality */ const fetchAssetCriticality = async ( - params: Pick + params: Pick ): Promise => { return http.fetch(ASSET_CRITICALITY_PUBLIC_URL, { version: API_VERSIONS.public.v1, - method: 'GET', + method: "GET", query: { id_value: params.idValue, id_field: params.idField }, }); }; @@ -182,17 +200,19 @@ export const useEntityAnalyticsRoutes = () => { fileContent: string, fileName: string ): Promise => { - const file = new File([new Blob([fileContent])], fileName, { type: 'text/csv' }); + const file = new File([new Blob([fileContent])], fileName, { + type: "text/csv", + }); const body = new FormData(); - body.append('file', file); + body.append("file", file); return http.fetch( ASSET_CRITICALITY_PUBLIC_CSV_UPLOAD_URL, { version: API_VERSIONS.public.v1, - method: 'POST', + method: "POST", headers: { - 'Content-Type': undefined, // Lets the browser set the appropriate content type + "Content-Type": undefined, // Lets the browser set the appropriate content type }, body, } @@ -212,21 +232,34 @@ export const useEntityAnalyticsRoutes = () => { isDeprecated: boolean; isEnabled: boolean; }> => - http.fetch<{ isDeprecated: boolean; isEnabled: boolean }>(RISK_SCORE_INDEX_STATUS_API_URL, { - version: '1', - method: 'GET', - query, - asSystemRequest: true, - signal, - }); + http.fetch<{ isDeprecated: boolean; isEnabled: boolean }>( + RISK_SCORE_INDEX_STATUS_API_URL, + { + version: "1", + method: "GET", + query, + asSystemRequest: true, + signal, + } + ); /** * Fetches risk engine settings */ const fetchRiskEngineSettings = () => http.fetch(RISK_ENGINE_SETTINGS_URL, { - version: '1', - method: 'GET', + version: "1", + method: "GET", + }); + + /** + * Deletes Risk engine installation and associated data + */ + + const deleteRiskEngineInstallationAndData = () => + http.fetch(RISK_ENGINE_INSTALLATION_AND_DATA_CLEANUP_URL, { + version: "1", + method: "DELETE", }); return { @@ -244,6 +277,7 @@ export const useEntityAnalyticsRoutes = () => { getRiskScoreIndexStatus, fetchRiskEngineSettings, calculateEntityRiskScore, + deleteRiskEngineInstallationAndData, }; }, [http]); }; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.mock.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.mock.ts index a8d7b7a9c763b..478805498815d 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.mock.ts @@ -5,10 +5,11 @@ * 2.0. */ -import type { RiskEngineDataClient } from './risk_engine_data_client'; +import type { RiskEngineDataClient } from "./risk_engine_data_client"; const createRiskEngineDataClientMock = () => ({ + cleanupRiskEngine: jest.fn(), disableLegacyRiskEngine: jest.fn(), disableRiskEngine: jest.fn(), enableRiskEngine: jest.fn(), @@ -17,4 +18,6 @@ const createRiskEngineDataClientMock = () => init: jest.fn(), } as unknown as jest.Mocked); -export const riskEngineDataClientMock = { create: createRiskEngineDataClientMock }; +export const riskEngineDataClientMock = { + create: createRiskEngineDataClientMock, +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts new file mode 100644 index 0000000000000..24df2be296b37 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts @@ -0,0 +1,101 @@ +/* + * 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 { taskManagerMock } from "@kbn/task-manager-plugin/server/mocks"; +import { RISK_ENGINE_INSTALLATION_AND_DATA_CLEANUP_URL } from "../../../../../common/constants"; +import { + serverMock, + requestContextMock, + requestMock, +} from "../../../detection_engine/routes/__mocks__"; +import { riskEngineDataClientMock } from "../risk_engine_data_client.mock"; + +describe("risk engine cleanup route", () => { + let server: ReturnType; + let context: ReturnType; + let mockTaskManagerStart: ReturnType; + let mockRiskEngineDataClient: ReturnType< + typeof riskEngineDataClientMock.create + >; + let getStartServicesMock: jest.Mock; + + beforeEach(() => { + jest.resetAllMocks(); + + server = serverMock.create(); + const { clients } = requestContextMock.createTools(); + mockRiskEngineDataClient = riskEngineDataClientMock.create(); + context = requestContextMock.convertContext( + requestContextMock.create({ + ...clients, + riskEngineDataClient: mockRiskEngineDataClient, + }) + ); + mockTaskManagerStart = taskManagerMock.createStart(); + }); + + const buildRequest = () => { + return requestMock.create({ + method: "delete", + path: RISK_ENGINE_INSTALLATION_AND_DATA_CLEANUP_URL, + body: {}, + }); + }; + describe("invokes the risk engine cleanup route", () => { + it("should call the router with the correct route and handler", async () => { + const request = buildRequest(); + await server.inject(request, context); + expect(mockRiskEngineDataClient.tearDown).toHaveBeenCalled(); + }); + }); + + // it('should return a 403 response if the user does not have the required privilege', async () => { + // const router = { + // delete: jest.fn(), + // }; + + // const getStartServices = jest.fn().mockResolvedValue([{ savedObjects: {} }, {}, {}]); + + // const handler = riskEngineCleanupRoute(router, getStartServices); + + // const request = { + // headers: {}, + // }; + + // const response = await handler(request); + + // expect(response.status).toBe(403); + // expect(response.payload).toEqual({ + // message: 'User does not have the required privilege to perform this action.', + // }); + // }); + + // it('should return a 500 response if the task manager is unavailable', async () => { + // const router = { + // delete: jest.fn(), + // }; + + // const getStartServices = jest.fn().mockResolvedValue([{ savedObjects: {} }, {}, {}]); + + // const handler = riskEngineCleanupRoute(router, getStartServices); + + // const request = { + // headers: { + // authorization: 'Bearer token', + // }, + // }; + + // const response = await handler(request); + + // expect(response.status).toBe(500); + // expect(response.payload).toEqual({ + // message: 'Task manager is unavailable. Please try again later.', + // }); + // }); + + // // Add more test cases here... +}); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts new file mode 100644 index 0000000000000..71fa7eac4cecb --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts @@ -0,0 +1,89 @@ +/* + * 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 { buildSiemResponse } from "@kbn/lists-plugin/server/routes/utils"; +import { transformError } from "@kbn/securitysolution-es-utils"; +import type { IKibanaResponse } from "@kbn/core-http-server"; +import { withRiskEnginePrivilegeCheck } from "../risk_engine_privileges"; +import { + RISK_ENGINE_INSTALLATION_AND_DATA_CLEANUP_URL, + APP_ID, +} from "../../../../../common/constants"; +import type { EntityAnalyticsRoutesDeps } from "../../types"; +import { RiskEngineAuditActions } from "../audit"; +import { AUDIT_CATEGORY, AUDIT_OUTCOME, AUDIT_TYPE } from "../../audit"; +import { TASK_MANAGER_UNAVAILABLE_ERROR } from "./translations"; + +export const riskEngineCleanupRoute = ( + router: EntityAnalyticsRoutesDeps["router"], + getStartServices: EntityAnalyticsRoutesDeps["getStartServices"] +) => { + router.versioned + .delete({ + access: "public", + path: RISK_ENGINE_INSTALLATION_AND_DATA_CLEANUP_URL, + options: { + tags: ["access:securitySolution", `access:${APP_ID}-entity-analytics`], + }, + }) + .addVersion( + { version: "1", validate: {} }, + withRiskEnginePrivilegeCheck( + getStartServices, + async ( + context, + request, + response + ): Promise> => { + const siemResponse = buildSiemResponse(response); + const securitySolution = await context.securitySolution; + const [_, { taskManager }] = await getStartServices(); + + if (!taskManager) { + securitySolution.getAuditLogger()?.log({ + message: + "User attempted to perform a cleanup of risk engine, but the Kibana Task Manager was unavailable", + event: { + action: RiskEngineAuditActions.RISK_ENGINE_REMOVE_TASK, + category: AUDIT_CATEGORY.DATABASE, + type: AUDIT_TYPE.DELETION, + outcome: AUDIT_OUTCOME.FAILURE, + }, + error: { + message: + "User attempted to perform a cleanup of risk engine, but the Kibana Task Manager was unavailable", + }, + }); + + return siemResponse.error({ + statusCode: 400, + body: TASK_MANAGER_UNAVAILABLE_ERROR, + }); + } + + const riskEngineClient = securitySolution.getRiskEngineDataClient(); + const riskScoreDataClient = securitySolution.getRiskScoreDataClient(); + + try { + await riskEngineClient.tearDown({ + taskManager, + riskScoreDataClient, + }); + + return response.ok({ body: { success: true } }); + } catch (e) { + const error = transformError(e); + return siemResponse.error({ + statusCode: error.statusCode, + body: { message: error.message, full_error: JSON.stringify(e) }, + bypassErrorFormat: true, + }); + } + } + ) + ); +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/register_risk_engine_routes.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/register_risk_engine_routes.ts index 73e4370a26285..4a95e0566b41b 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/register_risk_engine_routes.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/register_risk_engine_routes.ts @@ -4,13 +4,14 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { riskEngineInitRoute } from './init'; -import { riskEngineEnableRoute } from './enable'; -import { riskEngineDisableRoute } from './disable'; -import { riskEngineStatusRoute } from './status'; -import { riskEnginePrivilegesRoute } from './privileges'; -import { riskEngineSettingsRoute } from './settings'; -import type { EntityAnalyticsRoutesDeps } from '../../types'; +import { riskEngineInitRoute } from "./init"; +import { riskEngineEnableRoute } from "./enable"; +import { riskEngineDisableRoute } from "./disable"; +import { riskEngineStatusRoute } from "./status"; +import { riskEnginePrivilegesRoute } from "./privileges"; +import { riskEngineSettingsRoute } from "./settings"; +import type { EntityAnalyticsRoutesDeps } from "../../types"; +import { riskEngineCleanupRoute } from "./delete"; export const registerRiskEngineRoutes = ({ router, @@ -22,4 +23,5 @@ export const registerRiskEngineRoutes = ({ riskEngineDisableRoute(router, getStartServices); riskEngineSettingsRoute(router); riskEnginePrivilegesRoute(router, getStartServices); + riskEngineCleanupRoute(router, getStartServices); }; 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 198ffcfc0f54d..c95579d79ad72 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 @@ -22,6 +22,7 @@ import { RISK_ENGINE_ENABLE_URL, RISK_ENGINE_STATUS_URL, RISK_ENGINE_PRIVILEGES_URL, + RISK_ENGINE_INSTALLATION_AND_DATA_CLEANUP_URL, } from '@kbn/security-solution-plugin/common/constants'; import { MappingTypeMapping } from '@elastic/elasticsearch/lib/api/types'; import { removeLegacyTransforms } from '@kbn/security-solution-plugin/server/lib/entity_analytics/utils/transforms'; @@ -572,6 +573,15 @@ export const riskEngineRouteHelpersFactory = (supertest: SuperTest.Agent, namesp .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .send() .expect(expectStatusCode), + + delete: async (expectStatusCode: number = 200) => + await supertest + .delete(routeWithNamespace(RISK_ENGINE_INSTALLATION_AND_DATA_CLEANUP_URL, namespace)) + .set('kbn-xsrf', 'true') + .set('elastic-api-version', '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send() + .expect(expectStatusCode), }); interface Credentials { From 5b00e14c5da91bdbb25b472bec20c7baa9594ff0 Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Tue, 3 Sep 2024 19:07:56 +0530 Subject: [PATCH 02/25] Adding test cases for the changes --- .../risk_engine_data_client.mock.ts | 2 +- .../risk_engine/routes/delete.test.ts | 121 ++++++++++++------ .../risk_engine/routes/delete.ts | 3 +- .../routes/risk_engine_privileges.mock.ts | 27 +++- 4 files changed, 107 insertions(+), 46 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.mock.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.mock.ts index 478805498815d..b72d3645046ba 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.mock.ts @@ -9,13 +9,13 @@ import type { RiskEngineDataClient } from "./risk_engine_data_client"; const createRiskEngineDataClientMock = () => ({ - cleanupRiskEngine: jest.fn(), disableLegacyRiskEngine: jest.fn(), disableRiskEngine: jest.fn(), enableRiskEngine: jest.fn(), getConfiguration: jest.fn(), getStatus: jest.fn(), init: jest.fn(), + tearDown: jest.fn(), } as unknown as jest.Mocked); export const riskEngineDataClientMock = { diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts index 24df2be296b37..b653d90cb6e60 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts @@ -12,7 +12,9 @@ import { requestContextMock, requestMock, } from "../../../detection_engine/routes/__mocks__"; +import { riskEnginePrivilegesMock } from "./risk_engine_privileges.mock"; import { riskEngineDataClientMock } from "../risk_engine_data_client.mock"; +import { riskEngineCleanupRoute } from "./delete"; describe("risk engine cleanup route", () => { let server: ReturnType; @@ -46,56 +48,93 @@ describe("risk engine cleanup route", () => { }); }; describe("invokes the risk engine cleanup route", () => { + beforeEach(() => { + getStartServicesMock = jest.fn().mockResolvedValue([ + {}, + { + taskManager: mockTaskManagerStart, + security: + riskEnginePrivilegesMock.createMockSecurityStartWithFullRiskEngineAccess(), + }, + ]); + riskEngineCleanupRoute(server.router, getStartServicesMock); + }); + it("should call the router with the correct route and handler", async () => { const request = buildRequest(); await server.inject(request, context); expect(mockRiskEngineDataClient.tearDown).toHaveBeenCalled(); }); - }); - - // it('should return a 403 response if the user does not have the required privilege', async () => { - // const router = { - // delete: jest.fn(), - // }; - - // const getStartServices = jest.fn().mockResolvedValue([{ savedObjects: {} }, {}, {}]); - - // const handler = riskEngineCleanupRoute(router, getStartServices); - // const request = { - // headers: {}, - // }; - - // const response = await handler(request); - - // expect(response.status).toBe(403); - // expect(response.payload).toEqual({ - // message: 'User does not have the required privilege to perform this action.', - // }); - // }); - - // it('should return a 500 response if the task manager is unavailable', async () => { - // const router = { - // delete: jest.fn(), - // }; - - // const getStartServices = jest.fn().mockResolvedValue([{ savedObjects: {} }, {}, {}]); + it("returns a 200 when cleanup is successful", async () => { + const request = buildRequest(); + const response = await server.inject(request, context); + expect(response.status).toBe(200); + expect(response.body).toEqual({ success: true }); + }); - // const handler = riskEngineCleanupRoute(router, getStartServices); + it("returns a 500 when cleanup is unsuccessful", async () => { + mockRiskEngineDataClient.tearDown.mockImplementation(() => { + throw new Error("Error tearing down"); + }); + const request = buildRequest(); + const response = await server.inject(request, context); + expect(response.status).toBe(500); + expect(response.body).toEqual({ + full_error: "{}", + message: "Error tearing down", + status_code: 500, + }); + }); - // const request = { - // headers: { - // authorization: 'Bearer token', - // }, - // }; + // it("returns a 500 when ") + }); + describe("when task manager is unavailable", () => { + beforeEach(() => { + getStartServicesMock = jest.fn().mockResolvedValue([ + {}, + { + security: + riskEnginePrivilegesMock.createMockSecurityStartWithFullRiskEngineAccess(), + }, + ]); + riskEngineCleanupRoute(server.router, getStartServicesMock); + }); - // const response = await handler(request); + it("returns a 400 when task manager is unavailable", async () => { + const request = buildRequest(); + const response = await server.inject(request, context); + expect(response.status).toBe(400); + expect(response.body).toEqual({ + message: + "Task Manager is unavailable, but is required by the risk engine. Please enable the taskManager plugin and try again.", + status_code: 400, + }); + }); + }); - // expect(response.status).toBe(500); - // expect(response.payload).toEqual({ - // message: 'Task manager is unavailable. Please try again later.', - // }); - // }); + describe("when user does not have the required privileges", () => { + beforeEach(() => { + getStartServicesMock = jest.fn().mockResolvedValue([ + {}, + { + taskManager: mockTaskManagerStart, + security: + riskEnginePrivilegesMock.createMockSecurityStartWithNoRiskEngineAccess(), + }, + ]); + riskEngineCleanupRoute(server.router, getStartServicesMock); + }); - // // Add more test cases here... + it("returns a 403 when user does not have the required privileges", async () => { + const request = buildRequest(); + const response = await server.inject(request, context); + expect(response.status).toBe(403); + expect(response.body).toEqual({ + message: + "User is missing risk engine privileges. Missing cluster privileges: manage_index_templates, manage_transform.", + status_code: 403, + }); + }); + }); }); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts index 71fa7eac4cecb..8fb2bf8a56533 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts @@ -12,6 +12,7 @@ import { withRiskEnginePrivilegeCheck } from "../risk_engine_privileges"; import { RISK_ENGINE_INSTALLATION_AND_DATA_CLEANUP_URL, APP_ID, + API_VERSIONS, } from "../../../../../common/constants"; import type { EntityAnalyticsRoutesDeps } from "../../types"; import { RiskEngineAuditActions } from "../audit"; @@ -31,7 +32,7 @@ export const riskEngineCleanupRoute = ( }, }) .addVersion( - { version: "1", validate: {} }, + { version: API_VERSIONS.public.v1, validate: {} }, withRiskEnginePrivilegeCheck( getStartServices, async ( diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_privileges.mock.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_privileges.mock.ts index 10c772cfcf05e..d86cdeec4867b 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_privileges.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_privileges.mock.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { securityMock } from '@kbn/security-plugin/server/mocks'; +import { securityMock } from "@kbn/security-plugin/server/mocks"; const createMockSecurityStartWithFullRiskEngineAccess = () => { const mockSecurityStart = securityMock.createStart(); @@ -14,9 +14,9 @@ const createMockSecurityStartWithFullRiskEngineAccess = () => { hasAllRequested: true, privileges: { elasticsearch: { - cluster: ['manage', 'monitor'], + cluster: ["manage", "monitor"], index: { - 'index-name': ['read'], + "index-name": ["read"], }, }, }, @@ -29,6 +29,27 @@ const createMockSecurityStartWithFullRiskEngineAccess = () => { return mockSecurityStart; }; +const createMockSecurityStartWithNoRiskEngineAccess = () => { + const mockSecurityStart = securityMock.createStart(); + + const mockCheckPrivileges = jest.fn().mockResolvedValue({ + hasAllRequested: false, + privileges: { + elasticsearch: { + cluster: [], + index: [], + }, + }, + }); + + mockSecurityStart.authz.checkPrivilegesDynamicallyWithRequest = jest + .fn() + .mockReturnValue(mockCheckPrivileges); + + return mockSecurityStart; +}; + export const riskEnginePrivilegesMock = { createMockSecurityStartWithFullRiskEngineAccess, + createMockSecurityStartWithNoRiskEngineAccess, }; From cabaa192672da11ab4323e65f6194e51479d70b8 Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Wed, 4 Sep 2024 15:17:19 +0530 Subject: [PATCH 03/25] Adding a test case to check for multiple errors while tearing down the Risk Engine --- .../risk_engine/routes/delete.test.ts | 21 ++++++++++-- .../risk_engine/routes/delete.ts | 33 ++++++++++++------- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts index b653d90cb6e60..97613aa666ce4 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts @@ -82,12 +82,29 @@ describe("risk engine cleanup route", () => { expect(response.status).toBe(500); expect(response.body).toEqual({ full_error: "{}", - message: "Error tearing down", + message: "Error tearing down Risk Engine", status_code: 500, }); }); - // it("returns a 500 when ") + it("returns a 500 when cleanup is unsuccessful with multiple errors", async () => { + mockRiskEngineDataClient.tearDown.mockImplementation(async () => { + return [ + Error("Error while removing risk scoring task"), + Error("Error while deleting saved objects"), + Error("Error while removing risk score index"), + ]; + }); + const request = buildRequest(); + const response = await server.inject(request, context); + expect(response.status).toBe(500); + expect(response.body).toEqual({ + errors: + "Error: Error while removing risk scoring task\nError: Error while deleting saved objects\nError: Error while removing risk score index", + message: "Errors were encountered while tearing down Risk Engine", + status_code: 500, + }); + }); }); describe("when task manager is unavailable", () => { beforeEach(() => { diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts index 8fb2bf8a56533..82c9bc2d0934a 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts @@ -6,7 +6,6 @@ */ import { buildSiemResponse } from "@kbn/lists-plugin/server/routes/utils"; -import { transformError } from "@kbn/securitysolution-es-utils"; import type { IKibanaResponse } from "@kbn/core-http-server"; import { withRiskEnginePrivilegeCheck } from "../risk_engine_privileges"; import { @@ -43,6 +42,8 @@ export const riskEngineCleanupRoute = ( const siemResponse = buildSiemResponse(response); const securitySolution = await context.securitySolution; const [_, { taskManager }] = await getStartServices(); + const riskEngineClient = securitySolution.getRiskEngineDataClient(); + const riskScoreDataClient = securitySolution.getRiskScoreDataClient(); if (!taskManager) { securitySolution.getAuditLogger()?.log({ @@ -66,21 +67,31 @@ export const riskEngineCleanupRoute = ( }); } - const riskEngineClient = securitySolution.getRiskEngineDataClient(); - const riskScoreDataClient = securitySolution.getRiskScoreDataClient(); - try { - await riskEngineClient.tearDown({ + const errors = await riskEngineClient.tearDown({ taskManager, riskScoreDataClient, }); - - return response.ok({ body: { success: true } }); - } catch (e) { - const error = transformError(e); + if (errors) { + return siemResponse.error({ + statusCode: 500, + body: { + message: + "Errors were encountered while tearing down Risk Engine", + errors: errors.join("\n"), + }, + bypassErrorFormat: true, + }); + } else { + return response.ok({ body: { success: true } }); + } + } catch (error) { return siemResponse.error({ - statusCode: error.statusCode, - body: { message: error.message, full_error: JSON.stringify(e) }, + statusCode: 500, + body: { + message: "Error tearing down Risk Engine", + full_error: JSON.stringify(error), + }, bypassErrorFormat: true, }); } From 685a2c9a8b31b084bd992639b5835ed94a03cc9f Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Wed, 4 Sep 2024 15:21:41 +0530 Subject: [PATCH 04/25] Shortening the variable name for the cleanup URL --- .../common/entity_analytics/risk_engine/constants.ts | 2 +- .../lib/entity_analytics/risk_engine/routes/delete.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts index bcf141359ddca..d44d9222e4728 100644 --- a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts +++ b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts @@ -13,7 +13,7 @@ export const RISK_ENGINE_DISABLE_URL = `${RISK_ENGINE_URL}/disable` as const; export const RISK_ENGINE_PRIVILEGES_URL = `${RISK_ENGINE_URL}/privileges` as const; export const RISK_ENGINE_SETTINGS_URL = `${RISK_ENGINE_URL}/settings` as const; -export const RISK_ENGINE_INSTALLATION_AND_DATA_CLEANUP_URL = +export const RISK_ENGINE_CLEANUP_URL = `/api/risk_score/engine/dangerously_delete_data` as const; export const MAX_SPACES_COUNT = 1; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts index 97613aa666ce4..9463ec20e9d5b 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts @@ -6,7 +6,7 @@ */ import { taskManagerMock } from "@kbn/task-manager-plugin/server/mocks"; -import { RISK_ENGINE_INSTALLATION_AND_DATA_CLEANUP_URL } from "../../../../../common/constants"; +import { RISK_ENGINE_CLEANUP_URL } from "../../../../../common/constants"; import { serverMock, requestContextMock, @@ -43,7 +43,7 @@ describe("risk engine cleanup route", () => { const buildRequest = () => { return requestMock.create({ method: "delete", - path: RISK_ENGINE_INSTALLATION_AND_DATA_CLEANUP_URL, + path: RISK_ENGINE_CLEANUP_URL, body: {}, }); }; From cb25970f4832cf1248e7e93cfb717bc87cc1a9ae Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Thu, 5 Sep 2024 18:57:05 +0530 Subject: [PATCH 05/25] Adding openAPI spec for the new API endpoint and changes to error and response structure --- .../risk_engine/engine_cleanup_route.gen.ts | 37 ++++++++++++ .../engine_cleanup_route.schema.yaml | 60 +++++++++++++++++++ .../public/entity_analytics/api/api.ts | 14 ++--- .../risk_engine/routes/delete.test.ts | 27 +++++++-- .../risk_engine/routes/delete.ts | 26 ++++---- .../services/security_solution_api.gen.ts | 10 ++++ 6 files changed, 151 insertions(+), 23 deletions(-) create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_cleanup_route.gen.ts create mode 100644 x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_cleanup_route.schema.yaml diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_cleanup_route.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_cleanup_route.gen.ts new file mode 100644 index 0000000000000..10b62ac50f3a9 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_cleanup_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. + */ + +/* + * 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 { z } from "@kbn/zod"; + +export type CleanUpRiskEngineErrorResponse = z.infer< + typeof CleanUpRiskEngineErrorResponse +>; +export const CleanUpRiskEngineErrorResponse = z.object({ + risk_engine_cleanup: z.boolean(), + errors: z.array( + z.object({ + seq: z.number().int(), + error: z.string(), + }) + ), +}); + +export type CleanUpRiskEngineResponse = z.infer< + typeof CleanUpRiskEngineResponse +>; +export const CleanUpRiskEngineResponse = z.object({ + risk_engine_cleanup: z.boolean().optional(), +}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_cleanup_route.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_cleanup_route.schema.yaml new file mode 100644 index 0000000000000..965342206e963 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_cleanup_route.schema.yaml @@ -0,0 +1,60 @@ +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. +paths: + /api/risk_score/engine/dangerously_delete_data: + delete: + x-labels: [ess, serverless] + x-codegen-enabled: true + operationId: CleanUpRiskEngine + summary: Cleanup the Risk Engine + description: Cleaning up the the Risk Engine by removing the indices, mapping and transforms + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + risk_engine_cleanup: + type: boolean + '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/CleanUpRiskEngineErrorResponse' + +components: + schemas: + CleanUpRiskEngineErrorResponse: + type: object + required: + - risk_engine_cleanup + - errors + properties: + risk_engine_cleanup: + type: boolean + example: false + errors: + type: array + items: + type: object + required: + - seq + - error + properties: + seq: + type: integer + error: + type: string + 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 43a465c0d8e1a..7b7ab48fe1f6a 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 @@ -11,7 +11,7 @@ import type { DisableRiskEngineResponse } from "../../../common/api/entity_analy import type { RiskEngineStatusResponse } from "../../../common/api/entity_analytics/risk_engine/engine_status_route.gen"; import type { InitRiskEngineResponse } from "../../../common/api/entity_analytics/risk_engine/engine_init_route.gen"; import type { EnableRiskEngineResponse } from "../../../common/api/entity_analytics/risk_engine/engine_enable_route.gen"; -import type { RiskEngineScheduleNowResponse } from '../../../common/api/entity_analytics/risk_engine/engine_schedule_now_route.gen'; +import type { RiskEngineScheduleNowResponse } from "../../../common/api/entity_analytics/risk_engine/engine_schedule_now_route.gen"; import type { RiskScoresPreviewRequest, RiskScoresPreviewResponse, @@ -39,8 +39,8 @@ import { ASSET_CRITICALITY_PUBLIC_CSV_UPLOAD_URL, RISK_SCORE_ENTITY_CALCULATION_URL, API_VERSIONS, - RISK_ENGINE_INSTALLATION_AND_DATA_CLEANUP_URL, - RISK_ENGINE_SCHEDULE_NOW_URL + RISK_ENGINE_CLEANUP_URL, + RISK_ENGINE_SCHEDULE_NOW_URL, } from "../../../common/constants"; import type { SnakeToCamelCase } from "../common/utils"; import { useKibana } from "../../common/lib/kibana/kibana_react"; @@ -113,7 +113,7 @@ export const useEntityAnalyticsRoutes = () => { const scheduleNowRiskEngine = () => http.fetch(RISK_ENGINE_SCHEDULE_NOW_URL, { version: API_VERSIONS.public.v1, - method: 'POST', + method: "POST", }); /** @@ -267,8 +267,8 @@ export const useEntityAnalyticsRoutes = () => { * Deletes Risk engine installation and associated data */ - const deleteRiskEngineInstallationAndData = () => - http.fetch(RISK_ENGINE_INSTALLATION_AND_DATA_CLEANUP_URL, { + const CleanUpRiskEngine = () => + http.fetch(RISK_ENGINE_CLEANUP_URL, { version: "1", method: "DELETE", }); @@ -289,7 +289,7 @@ export const useEntityAnalyticsRoutes = () => { getRiskScoreIndexStatus, fetchRiskEngineSettings, calculateEntityRiskScore, - deleteRiskEngineInstallationAndData, + CleanUpRiskEngine, }; }, [http]); }; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts index 9463ec20e9d5b..118c8e31a58b0 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts @@ -70,7 +70,7 @@ describe("risk engine cleanup route", () => { const request = buildRequest(); const response = await server.inject(request, context); expect(response.status).toBe(200); - expect(response.body).toEqual({ success: true }); + expect(response.body).toEqual({ risk_engine_cleanup: true }); }); it("returns a 500 when cleanup is unsuccessful", async () => { @@ -81,8 +81,11 @@ describe("risk engine cleanup route", () => { const response = await server.inject(request, context); expect(response.status).toBe(500); expect(response.body).toEqual({ - full_error: "{}", - message: "Error tearing down Risk Engine", + errors: { + error: "{}", + seq: 1, + }, + risk_engine_cleanup: false, status_code: 500, }); }); @@ -99,9 +102,21 @@ describe("risk engine cleanup route", () => { const response = await server.inject(request, context); expect(response.status).toBe(500); expect(response.body).toEqual({ - errors: - "Error: Error while removing risk scoring task\nError: Error while deleting saved objects\nError: Error while removing risk score index", - message: "Errors were encountered while tearing down Risk Engine", + errors: [ + { + seq: 1, + error: "Error: Error while removing risk scoring task", + }, + { + seq: 2, + error: "Error: Error while deleting saved objects", + }, + { + seq: 3, + error: "Error: Error while removing risk score index", + }, + ], + risk_engine_cleanup: false, status_code: 500, }); }); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts index 82c9bc2d0934a..5e61e10381052 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts @@ -9,7 +9,7 @@ import { buildSiemResponse } from "@kbn/lists-plugin/server/routes/utils"; import type { IKibanaResponse } from "@kbn/core-http-server"; import { withRiskEnginePrivilegeCheck } from "../risk_engine_privileges"; import { - RISK_ENGINE_INSTALLATION_AND_DATA_CLEANUP_URL, + RISK_ENGINE_CLEANUP_URL, APP_ID, API_VERSIONS, } from "../../../../../common/constants"; @@ -17,6 +17,7 @@ import type { EntityAnalyticsRoutesDeps } from "../../types"; import { RiskEngineAuditActions } from "../audit"; import { AUDIT_CATEGORY, AUDIT_OUTCOME, AUDIT_TYPE } from "../../audit"; import { TASK_MANAGER_UNAVAILABLE_ERROR } from "./translations"; +import type { CleanUpRiskEngineResponse } from "../../../../../common/api/entity_analytics"; export const riskEngineCleanupRoute = ( router: EntityAnalyticsRoutesDeps["router"], @@ -25,7 +26,7 @@ export const riskEngineCleanupRoute = ( router.versioned .delete({ access: "public", - path: RISK_ENGINE_INSTALLATION_AND_DATA_CLEANUP_URL, + path: RISK_ENGINE_CLEANUP_URL, options: { tags: ["access:securitySolution", `access:${APP_ID}-entity-analytics`], }, @@ -38,7 +39,7 @@ export const riskEngineCleanupRoute = ( context, request, response - ): Promise> => { + ): Promise> => { const siemResponse = buildSiemResponse(response); const securitySolution = await context.securitySolution; const [_, { taskManager }] = await getStartServices(); @@ -72,25 +73,30 @@ export const riskEngineCleanupRoute = ( taskManager, riskScoreDataClient, }); - if (errors) { + if (errors && errors.length > 0) { return siemResponse.error({ statusCode: 500, body: { - message: - "Errors were encountered while tearing down Risk Engine", - errors: errors.join("\n"), + risk_engine_cleanup: false, + errors: errors.map((error, seq) => ({ + seq: seq + 1, + error: error.toString(), + })), }, bypassErrorFormat: true, }); } else { - return response.ok({ body: { success: true } }); + return response.ok({ body: { risk_engine_cleanup: true } }); } } catch (error) { return siemResponse.error({ statusCode: 500, body: { - message: "Error tearing down Risk Engine", - full_error: JSON.stringify(error), + risk_engine_cleanup: false, + errors: { + seq: 1, + error: JSON.stringify(error), + }, }, bypassErrorFormat: true, }); diff --git a/x-pack/test/api_integration/services/security_solution_api.gen.ts b/x-pack/test/api_integration/services/security_solution_api.gen.ts index cf9722e89b408..5180745375d89 100644 --- a/x-pack/test/api_integration/services/security_solution_api.gen.ts +++ b/x-pack/test/api_integration/services/security_solution_api.gen.ts @@ -235,6 +235,16 @@ after 30 days. It also deletes other artifacts specific to the migration impleme .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .send(props.body as object); }, + /** + * Cleaning up the the Risk Engine by removing the indices, mapping and transforms + */ + cleanUpRiskEngine() { + return supertest + .delete('/api/risk_score/engine/dangerously_delete_data') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '1') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); + }, createAlertsIndex() { return supertest .post('/api/detection_engine/index') From 5cc0dffb5a681ad54d64c8f209c5bc13e1382e5b Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Thu, 5 Sep 2024 19:08:14 +0530 Subject: [PATCH 06/25] Using the correct variable name --- .../test_suites/entity_analytics/utils/risk_engine.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 436f8b3e8649d..e7c09a7ca8841 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 @@ -22,7 +22,7 @@ import { RISK_ENGINE_ENABLE_URL, RISK_ENGINE_STATUS_URL, RISK_ENGINE_PRIVILEGES_URL, - RISK_ENGINE_INSTALLATION_AND_DATA_CLEANUP_URL, + RISK_ENGINE_CLEANUP_URL, RISK_ENGINE_SCHEDULE_NOW_URL, } from '@kbn/security-solution-plugin/common/constants'; import { MappingTypeMapping } from '@elastic/elasticsearch/lib/api/types'; @@ -577,9 +577,9 @@ export const riskEngineRouteHelpersFactory = (supertest: SuperTest.Agent, namesp delete: async (expectStatusCode: number = 200) => await supertest - .delete(routeWithNamespace(RISK_ENGINE_INSTALLATION_AND_DATA_CLEANUP_URL, namespace)) + .delete(routeWithNamespace(RISK_ENGINE_CLEANUP_URL, namespace)) .set('kbn-xsrf', 'true') - .set('elastic-api-version', '1') + .set('elastic-api-version', '1'), scheduleNow: async (expectStatusCode: number = 200) => await supertest From 2bf4316c87c0481ea92a4f3931c09e7a546d3b41 Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Fri, 6 Sep 2024 12:53:55 +0530 Subject: [PATCH 07/25] Adding the missing export --- .../api/entity_analytics/risk_engine/index.ts | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/index.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/index.ts index 94d587cd2bfc7..eab675276199c 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/index.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/index.ts @@ -5,13 +5,14 @@ * 2.0. */ -export * from './engine_settings_route.gen'; -export * from './engine_status_route.gen'; -export * from './engine_init_route.gen'; -export * from './engine_disable_route.gen'; -export * from './engine_enable_route.gen'; -export * from './engine_status_route.gen'; -export * from './calculation_route.gen'; -export * from './preview_route.gen'; -export * from './entity_calculation_route.gen'; -export * from './get_risk_engine_privileges.gen'; +export * from "./engine_settings_route.gen"; +export * from "./engine_status_route.gen"; +export * from "./engine_init_route.gen"; +export * from "./engine_disable_route.gen"; +export * from "./engine_enable_route.gen"; +export * from "./engine_status_route.gen"; +export * from "./calculation_route.gen"; +export * from "./preview_route.gen"; +export * from "./entity_calculation_route.gen"; +export * from "./get_risk_engine_privileges.gen"; +export * from "./engine_cleanup_route.gen"; From 8fcf60efef73359496776b966f48e5f11c30a4fa Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Thu, 12 Sep 2024 18:40:07 +0530 Subject: [PATCH 08/25] Adding a test case to return 400 if the API is called multiple times --- .../risk_engine/risk_engine_data_client.ts | 22 +++-- .../risk_engine/routes/delete.test.ts | 86 +++++++++++-------- .../risk_engine/routes/delete.ts | 46 +++++----- 3 files changed, 85 insertions(+), 69 deletions(-) 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 c520e3fa07763..189622fa800f4 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 @@ -252,17 +252,23 @@ export class RiskEngineDataClient { public async tearDown({ taskManager, riskScoreDataClient }: TearDownParams) { const errors: Error[] = []; const addError = (e: Error) => errors.push(e); + const riskEngineStatus = await this.getCurrentStatus(); - await removeRiskScoringTask({ - namespace: this.options.namespace, - taskManager, - logger: this.options.logger, - }).catch(addError); + if (riskEngineStatus === RiskEngineStatusEnum.ENABLED) { + await removeRiskScoringTask({ + namespace: this.options.namespace, + taskManager, + logger: this.options.logger, + }).catch(addError); - await deleteSavedObjects({ savedObjectsClient: this.options.soClient }).catch(addError); - const riskScoreErrors = await riskScoreDataClient.tearDown(); + await deleteSavedObjects({ savedObjectsClient: this.options.soClient }).catch(addError); + const riskScoreErrors = await riskScoreDataClient.tearDown(); + errors.concat(riskScoreErrors); + } else { + errors.push(new Error('Risk engine is disabled or deleted already.')); + } - return errors.concat(riskScoreErrors); + return errors; } public async disableLegacyRiskEngine({ namespace }: { namespace: string }) { diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts index 118c8e31a58b0..06ed704fb4b7a 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts @@ -5,24 +5,22 @@ * 2.0. */ -import { taskManagerMock } from "@kbn/task-manager-plugin/server/mocks"; -import { RISK_ENGINE_CLEANUP_URL } from "../../../../../common/constants"; +import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; +import { RISK_ENGINE_CLEANUP_URL } from '../../../../../common/constants'; import { serverMock, requestContextMock, requestMock, -} from "../../../detection_engine/routes/__mocks__"; -import { riskEnginePrivilegesMock } from "./risk_engine_privileges.mock"; -import { riskEngineDataClientMock } from "../risk_engine_data_client.mock"; -import { riskEngineCleanupRoute } from "./delete"; +} from '../../../detection_engine/routes/__mocks__'; +import { riskEnginePrivilegesMock } from './risk_engine_privileges.mock'; +import { riskEngineDataClientMock } from '../risk_engine_data_client.mock'; +import { riskEngineCleanupRoute } from './delete'; -describe("risk engine cleanup route", () => { +describe('risk engine cleanup route', () => { let server: ReturnType; let context: ReturnType; let mockTaskManagerStart: ReturnType; - let mockRiskEngineDataClient: ReturnType< - typeof riskEngineDataClientMock.create - >; + let mockRiskEngineDataClient: ReturnType; let getStartServicesMock: jest.Mock; beforeEach(() => { @@ -42,47 +40,65 @@ describe("risk engine cleanup route", () => { const buildRequest = () => { return requestMock.create({ - method: "delete", + method: 'delete', path: RISK_ENGINE_CLEANUP_URL, body: {}, }); }; - describe("invokes the risk engine cleanup route", () => { + describe('invokes the risk engine cleanup route', () => { beforeEach(() => { getStartServicesMock = jest.fn().mockResolvedValue([ {}, { taskManager: mockTaskManagerStart, - security: - riskEnginePrivilegesMock.createMockSecurityStartWithFullRiskEngineAccess(), + security: riskEnginePrivilegesMock.createMockSecurityStartWithFullRiskEngineAccess(), }, ]); riskEngineCleanupRoute(server.router, getStartServicesMock); }); - it("should call the router with the correct route and handler", async () => { + it('should call the router with the correct route and handler', async () => { const request = buildRequest(); await server.inject(request, context); expect(mockRiskEngineDataClient.tearDown).toHaveBeenCalled(); }); - it("returns a 200 when cleanup is successful", async () => { + it('returns a 200 when cleanup is successful', async () => { const request = buildRequest(); const response = await server.inject(request, context); expect(response.status).toBe(200); expect(response.body).toEqual({ risk_engine_cleanup: true }); }); - it("returns a 500 when cleanup is unsuccessful", async () => { + it('returns a 400 when cleanup endpoint is called multiple times', async () => { + mockRiskEngineDataClient.tearDown.mockImplementation(async () => { + return [Error('Risk engine is disabled or deleted already.')]; + }); + const request = buildRequest(); + const response = await server.inject(request, context); + expect(response.status).toBe(400); + expect(response.body).toEqual({ + risk_engine_cleanup: false, + errors: [ + { + seq: 1, + error: 'Error: Risk engine is disabled or deleted already.', + }, + ], + status_code: 400, + }); + }); + + it('returns a 500 when cleanup is unsuccessful', async () => { mockRiskEngineDataClient.tearDown.mockImplementation(() => { - throw new Error("Error tearing down"); + throw new Error('Error tearing down'); }); const request = buildRequest(); const response = await server.inject(request, context); expect(response.status).toBe(500); expect(response.body).toEqual({ errors: { - error: "{}", + error: '{}', seq: 1, }, risk_engine_cleanup: false, @@ -90,12 +106,12 @@ describe("risk engine cleanup route", () => { }); }); - it("returns a 500 when cleanup is unsuccessful with multiple errors", async () => { + it('returns a 500 when cleanup is unsuccessful with multiple errors', async () => { mockRiskEngineDataClient.tearDown.mockImplementation(async () => { return [ - Error("Error while removing risk scoring task"), - Error("Error while deleting saved objects"), - Error("Error while removing risk score index"), + Error('Error while removing risk scoring task'), + Error('Error while deleting saved objects'), + Error('Error while removing risk score index'), ]; }); const request = buildRequest(); @@ -105,15 +121,15 @@ describe("risk engine cleanup route", () => { errors: [ { seq: 1, - error: "Error: Error while removing risk scoring task", + error: 'Error: Error while removing risk scoring task', }, { seq: 2, - error: "Error: Error while deleting saved objects", + error: 'Error: Error while deleting saved objects', }, { seq: 3, - error: "Error: Error while removing risk score index", + error: 'Error: Error while removing risk score index', }, ], risk_engine_cleanup: false, @@ -121,50 +137,48 @@ describe("risk engine cleanup route", () => { }); }); }); - describe("when task manager is unavailable", () => { + describe('when task manager is unavailable', () => { beforeEach(() => { getStartServicesMock = jest.fn().mockResolvedValue([ {}, { - security: - riskEnginePrivilegesMock.createMockSecurityStartWithFullRiskEngineAccess(), + security: riskEnginePrivilegesMock.createMockSecurityStartWithFullRiskEngineAccess(), }, ]); riskEngineCleanupRoute(server.router, getStartServicesMock); }); - it("returns a 400 when task manager is unavailable", async () => { + it('returns a 400 when task manager is unavailable', async () => { const request = buildRequest(); const response = await server.inject(request, context); expect(response.status).toBe(400); expect(response.body).toEqual({ message: - "Task Manager is unavailable, but is required by the risk engine. Please enable the taskManager plugin and try again.", + 'Task Manager is unavailable, but is required by the risk engine. Please enable the taskManager plugin and try again.', status_code: 400, }); }); }); - describe("when user does not have the required privileges", () => { + describe('when user does not have the required privileges', () => { beforeEach(() => { getStartServicesMock = jest.fn().mockResolvedValue([ {}, { taskManager: mockTaskManagerStart, - security: - riskEnginePrivilegesMock.createMockSecurityStartWithNoRiskEngineAccess(), + security: riskEnginePrivilegesMock.createMockSecurityStartWithNoRiskEngineAccess(), }, ]); riskEngineCleanupRoute(server.router, getStartServicesMock); }); - it("returns a 403 when user does not have the required privileges", async () => { + it('returns a 403 when user does not have the required privileges', async () => { const request = buildRequest(); const response = await server.inject(request, context); expect(response.status).toBe(403); expect(response.body).toEqual({ message: - "User is missing risk engine privileges. Missing cluster privileges: manage_index_templates, manage_transform.", + 'User is missing risk engine privileges. Missing cluster privileges: manage_index_templates, manage_transform.', status_code: 403, }); }); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts index 5e61e10381052..df70561de972c 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts @@ -5,41 +5,33 @@ * 2.0. */ -import { buildSiemResponse } from "@kbn/lists-plugin/server/routes/utils"; -import type { IKibanaResponse } from "@kbn/core-http-server"; -import { withRiskEnginePrivilegeCheck } from "../risk_engine_privileges"; -import { - RISK_ENGINE_CLEANUP_URL, - APP_ID, - API_VERSIONS, -} from "../../../../../common/constants"; -import type { EntityAnalyticsRoutesDeps } from "../../types"; -import { RiskEngineAuditActions } from "../audit"; -import { AUDIT_CATEGORY, AUDIT_OUTCOME, AUDIT_TYPE } from "../../audit"; -import { TASK_MANAGER_UNAVAILABLE_ERROR } from "./translations"; -import type { CleanUpRiskEngineResponse } from "../../../../../common/api/entity_analytics"; +import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; +import type { IKibanaResponse } from '@kbn/core-http-server'; +import { withRiskEnginePrivilegeCheck } from '../risk_engine_privileges'; +import { RISK_ENGINE_CLEANUP_URL, APP_ID, API_VERSIONS } from '../../../../../common/constants'; +import type { EntityAnalyticsRoutesDeps } from '../../types'; +import { RiskEngineAuditActions } from '../audit'; +import { AUDIT_CATEGORY, AUDIT_OUTCOME, AUDIT_TYPE } from '../../audit'; +import { TASK_MANAGER_UNAVAILABLE_ERROR } from './translations'; +import type { CleanUpRiskEngineResponse } from '../../../../../common/api/entity_analytics'; export const riskEngineCleanupRoute = ( - router: EntityAnalyticsRoutesDeps["router"], - getStartServices: EntityAnalyticsRoutesDeps["getStartServices"] + router: EntityAnalyticsRoutesDeps['router'], + getStartServices: EntityAnalyticsRoutesDeps['getStartServices'] ) => { router.versioned .delete({ - access: "public", + access: 'public', path: RISK_ENGINE_CLEANUP_URL, options: { - tags: ["access:securitySolution", `access:${APP_ID}-entity-analytics`], + tags: ['access:securitySolution', `access:${APP_ID}-entity-analytics`], }, }) .addVersion( { version: API_VERSIONS.public.v1, validate: {} }, withRiskEnginePrivilegeCheck( getStartServices, - async ( - context, - request, - response - ): Promise> => { + async (context, request, response): Promise> => { const siemResponse = buildSiemResponse(response); const securitySolution = await context.securitySolution; const [_, { taskManager }] = await getStartServices(); @@ -49,7 +41,7 @@ export const riskEngineCleanupRoute = ( if (!taskManager) { securitySolution.getAuditLogger()?.log({ message: - "User attempted to perform a cleanup of risk engine, but the Kibana Task Manager was unavailable", + 'User attempted to perform a cleanup of risk engine, but the Kibana Task Manager was unavailable', event: { action: RiskEngineAuditActions.RISK_ENGINE_REMOVE_TASK, category: AUDIT_CATEGORY.DATABASE, @@ -58,7 +50,7 @@ export const riskEngineCleanupRoute = ( }, error: { message: - "User attempted to perform a cleanup of risk engine, but the Kibana Task Manager was unavailable", + 'User attempted to perform a cleanup of risk engine, but the Kibana Task Manager was unavailable', }, }); @@ -75,7 +67,11 @@ export const riskEngineCleanupRoute = ( }); if (errors && errors.length > 0) { return siemResponse.error({ - statusCode: 500, + statusCode: errors.some((error) => + error.message.includes('Risk engine is disabled or deleted already.') + ) + ? 400 + : 500, body: { risk_engine_cleanup: false, errors: errors.map((error, seq) => ({ From e6a3f20bf57e7ef715f9c3f2e1f5025c392c30b9 Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Thu, 12 Sep 2024 18:59:31 +0530 Subject: [PATCH 09/25] Adding this file as a re-commit with single quotes --- .../risk_engine/engine_cleanup_route.gen.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_cleanup_route.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_cleanup_route.gen.ts index 10b62ac50f3a9..ff489978dd0da 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_cleanup_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_cleanup_route.gen.ts @@ -14,11 +14,9 @@ * version: 1 */ -import { z } from "@kbn/zod"; +import { z } from '@kbn/zod'; -export type CleanUpRiskEngineErrorResponse = z.infer< - typeof CleanUpRiskEngineErrorResponse ->; +export type CleanUpRiskEngineErrorResponse = z.infer; export const CleanUpRiskEngineErrorResponse = z.object({ risk_engine_cleanup: z.boolean(), errors: z.array( @@ -29,9 +27,7 @@ export const CleanUpRiskEngineErrorResponse = z.object({ ), }); -export type CleanUpRiskEngineResponse = z.infer< - typeof CleanUpRiskEngineResponse ->; +export type CleanUpRiskEngineResponse = z.infer; export const CleanUpRiskEngineResponse = z.object({ risk_engine_cleanup: z.boolean().optional(), }); From 45de858d08e7e000b3673d11c6710cee2cd3a944 Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Thu, 12 Sep 2024 19:22:14 +0530 Subject: [PATCH 10/25] Reverting all double quotes to single quotes --- .../api/entity_analytics/risk_engine/index.ts | 22 +-- .../entity_analytics/risk_engine/constants.ts | 18 ++- .../public/entity_analytics/api/api.ts | 136 ++++++++---------- .../risk_engine_data_client.mock.ts | 2 +- .../routes/register_risk_engine_routes.ts | 16 +-- .../routes/risk_engine_privileges.mock.ts | 6 +- 6 files changed, 92 insertions(+), 108 deletions(-) diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/index.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/index.ts index eab675276199c..21dc89544c8d8 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/index.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/index.ts @@ -5,14 +5,14 @@ * 2.0. */ -export * from "./engine_settings_route.gen"; -export * from "./engine_status_route.gen"; -export * from "./engine_init_route.gen"; -export * from "./engine_disable_route.gen"; -export * from "./engine_enable_route.gen"; -export * from "./engine_status_route.gen"; -export * from "./calculation_route.gen"; -export * from "./preview_route.gen"; -export * from "./entity_calculation_route.gen"; -export * from "./get_risk_engine_privileges.gen"; -export * from "./engine_cleanup_route.gen"; +export * from './engine_settings_route.gen'; +export * from './engine_status_route.gen'; +export * from './engine_init_route.gen'; +export * from './engine_disable_route.gen'; +export * from './engine_enable_route.gen'; +export * from './engine_status_route.gen'; +export * from './calculation_route.gen'; +export * from './preview_route.gen'; +export * from './entity_calculation_route.gen'; +export * from './get_risk_engine_privileges.gen'; +export * from './engine_cleanup_route.gen'; diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts index 1d8647383439c..ba25bd677e06d 100644 --- a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts +++ b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts @@ -10,11 +10,9 @@ export const RISK_ENGINE_STATUS_URL = `${RISK_ENGINE_URL}/status` as const; export const RISK_ENGINE_INIT_URL = `${RISK_ENGINE_URL}/init` as const; export const RISK_ENGINE_ENABLE_URL = `${RISK_ENGINE_URL}/enable` as const; export const RISK_ENGINE_DISABLE_URL = `${RISK_ENGINE_URL}/disable` as const; -export const RISK_ENGINE_PRIVILEGES_URL = - `${RISK_ENGINE_URL}/privileges` as const; +export const RISK_ENGINE_PRIVILEGES_URL = `${RISK_ENGINE_URL}/privileges` as const; export const RISK_ENGINE_SETTINGS_URL = `${RISK_ENGINE_URL}/settings` as const; -export const RISK_ENGINE_CLEANUP_URL = - `/api/risk_score/engine/dangerously_delete_data` as const; +export const RISK_ENGINE_CLEANUP_URL = `/api/risk_score/engine/dangerously_delete_data` as const; // Public Risk Score routes export const PUBLIC_RISK_ENGINE_URL = `${PUBLIC_RISK_SCORE_URL}/engine` as const; @@ -22,16 +20,16 @@ export const RISK_ENGINE_SCHEDULE_NOW_URL = `${RISK_ENGINE_URL}/schedule_now` as export const MAX_SPACES_COUNT = 1; -type ClusterPrivilege = "manage_index_templates" | "manage_transform"; +type ClusterPrivilege = 'manage_index_templates' | 'manage_transform'; export const RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES = [ - "manage_index_templates", - "manage_transform", + 'manage_index_templates', + 'manage_transform', ] as ClusterPrivilege[]; -export const RISK_SCORE_INDEX_PATTERN = "risk-score.risk-score-*"; +export const RISK_SCORE_INDEX_PATTERN = 'risk-score.risk-score-*'; -export type RiskEngineIndexPrivilege = "read" | "write"; +export type RiskEngineIndexPrivilege = 'read' | 'write'; export const RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES = Object.freeze({ - [RISK_SCORE_INDEX_PATTERN]: ["read", "write"] as RiskEngineIndexPrivilege[], + [RISK_SCORE_INDEX_PATTERN]: ['read', 'write'] as RiskEngineIndexPrivilege[], }); 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 7b7ab48fe1f6a..7a0e4149bbd1a 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 @@ -5,26 +5,26 @@ * 2.0. */ -import { useMemo } from "react"; -import type { UploadAssetCriticalityRecordsResponse } from "../../../common/api/entity_analytics/asset_criticality/upload_asset_criticality_csv.gen"; -import type { DisableRiskEngineResponse } 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 { InitRiskEngineResponse } from "../../../common/api/entity_analytics/risk_engine/engine_init_route.gen"; -import type { EnableRiskEngineResponse } from "../../../common/api/entity_analytics/risk_engine/engine_enable_route.gen"; -import type { RiskEngineScheduleNowResponse } from "../../../common/api/entity_analytics/risk_engine/engine_schedule_now_route.gen"; +import { useMemo } from 'react'; +import type { UploadAssetCriticalityRecordsResponse } from '../../../common/api/entity_analytics/asset_criticality/upload_asset_criticality_csv.gen'; +import type { DisableRiskEngineResponse } 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 { InitRiskEngineResponse } from '../../../common/api/entity_analytics/risk_engine/engine_init_route.gen'; +import type { EnableRiskEngineResponse } from '../../../common/api/entity_analytics/risk_engine/engine_enable_route.gen'; +import type { RiskEngineScheduleNowResponse } from '../../../common/api/entity_analytics/risk_engine/engine_schedule_now_route.gen'; import type { RiskScoresPreviewRequest, RiskScoresPreviewResponse, -} from "../../../common/api/entity_analytics/risk_engine/preview_route.gen"; +} 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"; +} from '../../../common/api/entity_analytics/risk_engine/entity_calculation_route.gen'; import type { AssetCriticalityRecord, EntityAnalyticsPrivileges, -} from "../../../common/api/entity_analytics"; -import type { RiskScoreEntity } from "../../../common/search_strategy"; +} from '../../../common/api/entity_analytics'; +import type { RiskScoreEntity } from '../../../common/search_strategy'; import { RISK_ENGINE_STATUS_URL, RISK_SCORE_PREVIEW_URL, @@ -41,10 +41,10 @@ import { API_VERSIONS, RISK_ENGINE_CLEANUP_URL, RISK_ENGINE_SCHEDULE_NOW_URL, -} from "../../../common/constants"; -import type { SnakeToCamelCase } from "../common/utils"; -import { useKibana } from "../../common/lib/kibana/kibana_react"; -import type { ReadRiskEngineSettingsResponse } from "../../../common/api/entity_analytics/risk_engine"; +} from '../../../common/constants'; +import type { SnakeToCamelCase } from '../common/utils'; +import { useKibana } from '../../common/lib/kibana/kibana_react'; +import type { ReadRiskEngineSettingsResponse } from '../../../common/api/entity_analytics/risk_engine'; export interface DeleteAssetCriticalityResponse { deleted: true; @@ -64,8 +64,8 @@ export const useEntityAnalyticsRoutes = () => { params: RiskScoresPreviewRequest; }) => http.fetch(RISK_SCORE_PREVIEW_URL, { - version: "1", - method: "POST", + version: '1', + method: 'POST', body: JSON.stringify(params), signal, }); @@ -75,8 +75,8 @@ export const useEntityAnalyticsRoutes = () => { */ const fetchRiskEngineStatus = ({ signal }: { signal?: AbortSignal }) => http.fetch(RISK_ENGINE_STATUS_URL, { - version: "1", - method: "GET", + version: '1', + method: 'GET', signal, }); @@ -85,8 +85,8 @@ export const useEntityAnalyticsRoutes = () => { */ const initRiskEngine = () => http.fetch(RISK_ENGINE_INIT_URL, { - version: "1", - method: "POST", + version: '1', + method: 'POST', }); /** @@ -94,8 +94,8 @@ export const useEntityAnalyticsRoutes = () => { */ const enableRiskEngine = () => http.fetch(RISK_ENGINE_ENABLE_URL, { - version: "1", - method: "POST", + version: '1', + method: 'POST', }); /** @@ -103,8 +103,8 @@ export const useEntityAnalyticsRoutes = () => { */ const disableRiskEngine = () => http.fetch(RISK_ENGINE_DISABLE_URL, { - version: "1", - method: "POST", + version: '1', + method: 'POST', }); /** @@ -113,23 +113,18 @@ export const useEntityAnalyticsRoutes = () => { const scheduleNowRiskEngine = () => http.fetch(RISK_ENGINE_SCHEDULE_NOW_URL, { version: API_VERSIONS.public.v1, - method: "POST", + method: 'POST', }); /** * Calculate and stores risk score for an entity */ - const calculateEntityRiskScore = ( - params: RiskScoresEntityCalculationRequest - ) => { - return http.fetch( - RISK_SCORE_ENTITY_CALCULATION_URL, - { - version: "1", - method: "POST", - body: JSON.stringify(params), - } - ); + const calculateEntityRiskScore = (params: RiskScoresEntityCalculationRequest) => { + return http.fetch(RISK_SCORE_ENTITY_CALCULATION_URL, { + version: '1', + method: 'POST', + body: JSON.stringify(params), + }); }; /** @@ -137,36 +132,30 @@ export const useEntityAnalyticsRoutes = () => { */ const fetchRiskEnginePrivileges = () => http.fetch(RISK_ENGINE_PRIVILEGES_URL, { - version: "1", - method: "GET", + version: '1', + method: 'GET', }); /** * Get asset criticality privileges */ const fetchAssetCriticalityPrivileges = () => - http.fetch( - ASSET_CRITICALITY_INTERNAL_PRIVILEGES_URL, - { - version: "1", - method: "GET", - } - ); + http.fetch(ASSET_CRITICALITY_INTERNAL_PRIVILEGES_URL, { + version: '1', + method: 'GET', + }); /** * Create asset criticality */ const createAssetCriticality = async ( - params: Pick< - AssetCriticality, - "idField" | "idValue" | "criticalityLevel" - > & { - refresh?: "wait_for"; + params: Pick & { + refresh?: 'wait_for'; } ): Promise => http.fetch(ASSET_CRITICALITY_PUBLIC_URL, { version: API_VERSIONS.public.v1, - method: "POST", + method: 'POST', body: JSON.stringify({ id_value: params.idValue, id_field: params.idField, @@ -176,13 +165,13 @@ export const useEntityAnalyticsRoutes = () => { }); const deleteAssetCriticality = async ( - params: Pick & { - refresh?: "wait_for"; + params: Pick & { + refresh?: 'wait_for'; } ): Promise<{ deleted: true }> => { await http.fetch(ASSET_CRITICALITY_PUBLIC_URL, { version: API_VERSIONS.public.v1, - method: "DELETE", + method: 'DELETE', query: { id_value: params.idValue, id_field: params.idField, @@ -198,11 +187,11 @@ export const useEntityAnalyticsRoutes = () => { * Get asset criticality */ const fetchAssetCriticality = async ( - params: Pick + params: Pick ): Promise => { return http.fetch(ASSET_CRITICALITY_PUBLIC_URL, { version: API_VERSIONS.public.v1, - method: "GET", + method: 'GET', query: { id_value: params.idValue, id_field: params.idField }, }); }; @@ -212,18 +201,18 @@ export const useEntityAnalyticsRoutes = () => { fileName: string ): Promise => { const file = new File([new Blob([fileContent])], fileName, { - type: "text/csv", + type: 'text/csv', }); const body = new FormData(); - body.append("file", file); + body.append('file', file); return http.fetch( ASSET_CRITICALITY_PUBLIC_CSV_UPLOAD_URL, { version: API_VERSIONS.public.v1, - method: "POST", + method: 'POST', headers: { - "Content-Type": undefined, // Lets the browser set the appropriate content type + 'Content-Type': undefined, // Lets the browser set the appropriate content type }, body, } @@ -243,24 +232,21 @@ export const useEntityAnalyticsRoutes = () => { isDeprecated: boolean; isEnabled: boolean; }> => - http.fetch<{ isDeprecated: boolean; isEnabled: boolean }>( - RISK_SCORE_INDEX_STATUS_API_URL, - { - version: "1", - method: "GET", - query, - asSystemRequest: true, - signal, - } - ); + http.fetch<{ isDeprecated: boolean; isEnabled: boolean }>(RISK_SCORE_INDEX_STATUS_API_URL, { + version: '1', + method: 'GET', + query, + asSystemRequest: true, + signal, + }); /** * Fetches risk engine settings */ const fetchRiskEngineSettings = () => http.fetch(RISK_ENGINE_SETTINGS_URL, { - version: "1", - method: "GET", + version: '1', + method: 'GET', }); /** @@ -269,8 +255,8 @@ export const useEntityAnalyticsRoutes = () => { const CleanUpRiskEngine = () => http.fetch(RISK_ENGINE_CLEANUP_URL, { - version: "1", - method: "DELETE", + version: '1', + method: 'DELETE', }); return { diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.mock.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.mock.ts index b72d3645046ba..e9819c5b290d3 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.mock.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { RiskEngineDataClient } from "./risk_engine_data_client"; +import type { RiskEngineDataClient } from './risk_engine_data_client'; const createRiskEngineDataClientMock = () => ({ diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/register_risk_engine_routes.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/register_risk_engine_routes.ts index 01311ff2dcfda..f4edb7d798188 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/register_risk_engine_routes.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/register_risk_engine_routes.ts @@ -4,15 +4,15 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { riskEngineInitRoute } from "./init"; -import { riskEngineEnableRoute } from "./enable"; -import { riskEngineDisableRoute } from "./disable"; -import { riskEngineStatusRoute } from "./status"; -import { riskEnginePrivilegesRoute } from "./privileges"; -import { riskEngineSettingsRoute } from "./settings"; -import type { EntityAnalyticsRoutesDeps } from "../../types"; +import { riskEngineInitRoute } from './init'; +import { riskEngineEnableRoute } from './enable'; +import { riskEngineDisableRoute } from './disable'; +import { riskEngineStatusRoute } from './status'; +import { riskEnginePrivilegesRoute } from './privileges'; +import { riskEngineSettingsRoute } from './settings'; +import type { EntityAnalyticsRoutesDeps } from '../../types'; import { riskEngineScheduleNowRoute } from './schedule_now'; -import { riskEngineCleanupRoute } from "./delete"; +import { riskEngineCleanupRoute } from './delete'; export const registerRiskEngineRoutes = ({ router, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_privileges.mock.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_privileges.mock.ts index d86cdeec4867b..189e72624c15c 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_privileges.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_privileges.mock.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { securityMock } from "@kbn/security-plugin/server/mocks"; +import { securityMock } from '@kbn/security-plugin/server/mocks'; const createMockSecurityStartWithFullRiskEngineAccess = () => { const mockSecurityStart = securityMock.createStart(); @@ -14,9 +14,9 @@ const createMockSecurityStartWithFullRiskEngineAccess = () => { hasAllRequested: true, privileges: { elasticsearch: { - cluster: ["manage", "monitor"], + cluster: ['manage', 'monitor'], index: { - "index-name": ["read"], + 'index-name': ['read'], }, }, }, From 785a6113a8b9c5e5b8fefe14caeaeaac060648c6 Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Thu, 12 Sep 2024 20:20:56 +0530 Subject: [PATCH 11/25] Adding a file generated by openapi:generate --- .../common/api/quickstart_client.gen.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts b/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts index edd0bfe89fc8c..095eea97f739b 100644 --- a/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts +++ b/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts @@ -242,6 +242,7 @@ import type { InternalUploadAssetCriticalityRecordsResponse, UploadAssetCriticalityRecordsResponse, } from './entity_analytics/asset_criticality/upload_asset_criticality_csv.gen'; +import type { CleanUpRiskEngineResponse } from './entity_analytics/risk_engine/engine_cleanup_route.gen'; import type { DisableRiskEngineResponse } from './entity_analytics/risk_engine/engine_disable_route.gen'; import type { EnableRiskEngineResponse } from './entity_analytics/risk_engine/engine_enable_route.gen'; import type { InitRiskEngineResponse } from './entity_analytics/risk_engine/engine_init_route.gen'; @@ -500,6 +501,21 @@ after 30 days. It also deletes other artifacts specific to the migration impleme }) .catch(catchAxiosErrorFormatAndThrow); } + /** + * Cleaning up the the Risk Engine by removing the indices, mapping and transforms + */ + async cleanUpRiskEngine() { + this.log.info(`${new Date().toISOString()} Calling API CleanUpRiskEngine`); + return this.kbnClient + .request({ + path: '/api/risk_score/engine/dangerously_delete_data', + headers: { + [ELASTIC_HTTP_VERSION_HEADER]: '1', + }, + method: 'DELETE', + }) + .catch(catchAxiosErrorFormatAndThrow); + } async createAlertsIndex() { this.log.info(`${new Date().toISOString()} Calling API CreateAlertsIndex`); return this.kbnClient From 2655f7d52389c57ca56e10cd1af007e54fe2ff55 Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Mon, 16 Sep 2024 17:37:15 +0530 Subject: [PATCH 12/25] Modifying old unit tests to support changes to the new tearDown function --- .../risk_engine_data_client.test.ts | 36 +++++++++++++------ .../risk_engine/risk_engine_data_client.ts | 2 +- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.test.ts index 0f807ebe22265..ed92d2135c368 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.test.ts @@ -18,6 +18,7 @@ import type { RiskEngineConfiguration } from '../types'; import * as savedObjectConfig from './utils/saved_object_configuration'; import * as transforms from '../utils/transforms'; import { riskScoreDataClientMock } from '../risk_score/risk_score_data_client.mock'; +import { RiskEngineStatusEnum } from '../../../../common/api/entity_analytics'; const getSavedObjectConfiguration = (attributes = {}) => ({ page: 1, @@ -359,37 +360,50 @@ describe('RiskEngineDataClient', () => { }); describe('tearDownRiskEngine', () => { - const mockTaskManagerStart = taskManagerMock.createStart(); + let riskEngineCurrentStatusMock: jest.SpyInstance; + + beforeEach(() => { + riskEngineCurrentStatusMock = jest.spyOn( + RiskEngineDataClient.prototype, + 'getCurrentStatus' + ); + }); it('should delete the risk engine object and task if it exists', async () => { + const mockTaskManagerStart = taskManagerMock.createStart(); + riskEngineCurrentStatusMock.mockImplementation(() => { + return RiskEngineStatusEnum.ENABLED; + }); mockSavedObjectClient.find.mockResolvedValueOnce(getSavedObjectConfiguration()); const riskScoreDataClient = riskScoreDataClientMock.create(); await riskEngineDataClient.tearDown({ taskManager: mockTaskManagerStart, riskScoreDataClient, }); - expect(mockSavedObjectClient.delete).toHaveBeenCalledTimes(1); expect(mockTaskManagerStart.remove).toHaveBeenCalledTimes(1); expect(riskScoreDataClient.tearDown).toHaveBeenCalledTimes(1); }); - it('should return errors when exception is thrown ', async () => { - const error = new Error('testError'); - mockSavedObjectClient.find.mockResolvedValueOnce(getSavedObjectConfiguration()); - mockTaskManagerStart.remove.mockRejectedValueOnce(error); - mockSavedObjectClient.delete.mockRejectedValueOnce(error); - + it('should return errors when engine is disabled ', async () => { + riskEngineCurrentStatusMock.mockImplementation(() => { + return RiskEngineStatusEnum.DISABLED; + }); + const error = new Error('Risk engine is disabled or deleted already.'); + const mockTaskManagerStart = taskManagerMock.createStart(); const errors = await riskEngineDataClient.tearDown({ taskManager: mockTaskManagerStart, riskScoreDataClient: riskScoreDataClientMock.create(), }); - - expect(errors).toEqual([error, error]); + expect(errors).toEqual([error]); }); it('should return errors from riskScoreDataClient.tearDown ', async () => { - const error = new Error('testError'); + riskEngineCurrentStatusMock.mockImplementation(() => { + return RiskEngineStatusEnum.DISABLED; + }); + const error = new Error('Risk engine is disabled or deleted already.'); + const mockTaskManagerStart = taskManagerMock.createStart(); mockSavedObjectClient.find.mockResolvedValueOnce(getSavedObjectConfiguration()); const riskScoreDataClient = riskScoreDataClientMock.create(); riskScoreDataClient.tearDown.mockResolvedValueOnce([error]); 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 189622fa800f4..80f5366cbe75d 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 @@ -265,7 +265,7 @@ export class RiskEngineDataClient { const riskScoreErrors = await riskScoreDataClient.tearDown(); errors.concat(riskScoreErrors); } else { - errors.push(new Error('Risk engine is disabled or deleted already.')); + errors.push(Error('Risk engine is disabled or deleted already.')); } return errors; From c58887778c5c5b1624c77cdb2f4c3ff341dd4d79 Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Tue, 17 Sep 2024 13:42:01 +0530 Subject: [PATCH 13/25] Modified test case to access the private method for mocking --- .../risk_engine/risk_engine_data_client.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.test.ts index ed92d2135c368..f4936ae1af80b 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.test.ts @@ -364,7 +364,7 @@ describe('RiskEngineDataClient', () => { beforeEach(() => { riskEngineCurrentStatusMock = jest.spyOn( - RiskEngineDataClient.prototype, + RiskEngineDataClient.prototype as unknown as { getCurrentStatus: () => string }, 'getCurrentStatus' ); }); From da90fd2b2bb0e8d1150b9197de71939fbd756308 Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Tue, 17 Sep 2024 13:53:31 +0530 Subject: [PATCH 14/25] Renaming 'risk_engine_cleanup' to 'cleanup_successful' --- .../risk_engine/engine_cleanup_route.gen.ts | 4 ++-- .../risk_engine/engine_cleanup_route.schema.yaml | 6 +++--- .../security_solution/common/api/quickstart_client.gen.ts | 4 +--- .../entity_analytics/risk_engine/routes/delete.test.ts | 8 ++++---- .../lib/entity_analytics/risk_engine/routes/delete.ts | 6 +++--- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_cleanup_route.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_cleanup_route.gen.ts index ff489978dd0da..13194051244cb 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_cleanup_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_cleanup_route.gen.ts @@ -18,7 +18,7 @@ import { z } from '@kbn/zod'; export type CleanUpRiskEngineErrorResponse = z.infer; export const CleanUpRiskEngineErrorResponse = z.object({ - risk_engine_cleanup: z.boolean(), + cleanup_successful: z.boolean(), errors: z.array( z.object({ seq: z.number().int(), @@ -29,5 +29,5 @@ export const CleanUpRiskEngineErrorResponse = z.object({ export type CleanUpRiskEngineResponse = z.infer; export const CleanUpRiskEngineResponse = z.object({ - risk_engine_cleanup: z.boolean().optional(), + cleanup_successful: z.boolean().optional(), }); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_cleanup_route.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_cleanup_route.schema.yaml index 965342206e963..2dffe3879961e 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_cleanup_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/risk_engine/engine_cleanup_route.schema.yaml @@ -19,7 +19,7 @@ paths: schema: type: object properties: - risk_engine_cleanup: + cleanup_successful: type: boolean '400': description: Task manager is unavailable @@ -39,10 +39,10 @@ components: CleanUpRiskEngineErrorResponse: type: object required: - - risk_engine_cleanup + - cleanup_successful - errors properties: - risk_engine_cleanup: + cleanup_successful: type: boolean example: false errors: diff --git a/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts b/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts index a426ae4896790..d757656f4d8f6 100644 --- a/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts +++ b/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts @@ -242,9 +242,6 @@ import type { InternalUploadAssetCriticalityRecordsResponse, UploadAssetCriticalityRecordsResponse, } from './entity_analytics/asset_criticality/upload_asset_criticality_csv.gen'; -import type { - CleanUpRiskEngineResponse -} from './entity_analytics/risk_engine/engine_cleanup_route.gen'; import type { DeleteEntityStoreRequestQueryInput, DeleteEntityStoreRequestParamsInput, @@ -272,6 +269,7 @@ import type { StopEntityStoreRequestParamsInput, StopEntityStoreResponse, } from './entity_analytics/entity_store/engine/stop.gen'; +import type { CleanUpRiskEngineResponse } from './entity_analytics/risk_engine/engine_cleanup_route.gen'; import type { DisableRiskEngineResponse } from './entity_analytics/risk_engine/engine_disable_route.gen'; import type { EnableRiskEngineResponse } from './entity_analytics/risk_engine/engine_enable_route.gen'; import type { InitRiskEngineResponse } from './entity_analytics/risk_engine/engine_init_route.gen'; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts index 06ed704fb4b7a..5c66b70c75c13 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.test.ts @@ -67,7 +67,7 @@ describe('risk engine cleanup route', () => { const request = buildRequest(); const response = await server.inject(request, context); expect(response.status).toBe(200); - expect(response.body).toEqual({ risk_engine_cleanup: true }); + expect(response.body).toEqual({ cleanup_successful: true }); }); it('returns a 400 when cleanup endpoint is called multiple times', async () => { @@ -78,7 +78,7 @@ describe('risk engine cleanup route', () => { const response = await server.inject(request, context); expect(response.status).toBe(400); expect(response.body).toEqual({ - risk_engine_cleanup: false, + cleanup_successful: false, errors: [ { seq: 1, @@ -101,7 +101,7 @@ describe('risk engine cleanup route', () => { error: '{}', seq: 1, }, - risk_engine_cleanup: false, + cleanup_successful: false, status_code: 500, }); }); @@ -132,7 +132,7 @@ describe('risk engine cleanup route', () => { error: 'Error: Error while removing risk score index', }, ], - risk_engine_cleanup: false, + cleanup_successful: false, status_code: 500, }); }); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts index df70561de972c..1776ddcca69b1 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/delete.ts @@ -73,7 +73,7 @@ export const riskEngineCleanupRoute = ( ? 400 : 500, body: { - risk_engine_cleanup: false, + cleanup_successful: false, errors: errors.map((error, seq) => ({ seq: seq + 1, error: error.toString(), @@ -82,13 +82,13 @@ export const riskEngineCleanupRoute = ( bypassErrorFormat: true, }); } else { - return response.ok({ body: { risk_engine_cleanup: true } }); + return response.ok({ body: { cleanup_successful: true } }); } } catch (error) { return siemResponse.error({ statusCode: 500, body: { - risk_engine_cleanup: false, + cleanup_successful: false, errors: { seq: 1, error: JSON.stringify(error), From 92e19cedff494e8e88a622898dea4348efe09421 Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Wed, 18 Sep 2024 09:47:04 +0530 Subject: [PATCH 15/25] Removing the condition to check for enabled state of risk engine before deleting --- .../risk_engine_data_client.test.ts | 36 ++++++------------- .../risk_engine/risk_engine_data_client.ts | 23 +++++------- 2 files changed, 20 insertions(+), 39 deletions(-) diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.test.ts index f4936ae1af80b..0f807ebe22265 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.test.ts @@ -18,7 +18,6 @@ import type { RiskEngineConfiguration } from '../types'; import * as savedObjectConfig from './utils/saved_object_configuration'; import * as transforms from '../utils/transforms'; import { riskScoreDataClientMock } from '../risk_score/risk_score_data_client.mock'; -import { RiskEngineStatusEnum } from '../../../../common/api/entity_analytics'; const getSavedObjectConfiguration = (attributes = {}) => ({ page: 1, @@ -360,50 +359,37 @@ describe('RiskEngineDataClient', () => { }); describe('tearDownRiskEngine', () => { - let riskEngineCurrentStatusMock: jest.SpyInstance; - - beforeEach(() => { - riskEngineCurrentStatusMock = jest.spyOn( - RiskEngineDataClient.prototype as unknown as { getCurrentStatus: () => string }, - 'getCurrentStatus' - ); - }); + const mockTaskManagerStart = taskManagerMock.createStart(); it('should delete the risk engine object and task if it exists', async () => { - const mockTaskManagerStart = taskManagerMock.createStart(); - riskEngineCurrentStatusMock.mockImplementation(() => { - return RiskEngineStatusEnum.ENABLED; - }); mockSavedObjectClient.find.mockResolvedValueOnce(getSavedObjectConfiguration()); const riskScoreDataClient = riskScoreDataClientMock.create(); await riskEngineDataClient.tearDown({ taskManager: mockTaskManagerStart, riskScoreDataClient, }); + expect(mockSavedObjectClient.delete).toHaveBeenCalledTimes(1); expect(mockTaskManagerStart.remove).toHaveBeenCalledTimes(1); expect(riskScoreDataClient.tearDown).toHaveBeenCalledTimes(1); }); - it('should return errors when engine is disabled ', async () => { - riskEngineCurrentStatusMock.mockImplementation(() => { - return RiskEngineStatusEnum.DISABLED; - }); - const error = new Error('Risk engine is disabled or deleted already.'); - const mockTaskManagerStart = taskManagerMock.createStart(); + it('should return errors when exception is thrown ', async () => { + const error = new Error('testError'); + mockSavedObjectClient.find.mockResolvedValueOnce(getSavedObjectConfiguration()); + mockTaskManagerStart.remove.mockRejectedValueOnce(error); + mockSavedObjectClient.delete.mockRejectedValueOnce(error); + const errors = await riskEngineDataClient.tearDown({ taskManager: mockTaskManagerStart, riskScoreDataClient: riskScoreDataClientMock.create(), }); - expect(errors).toEqual([error]); + + expect(errors).toEqual([error, error]); }); it('should return errors from riskScoreDataClient.tearDown ', async () => { - riskEngineCurrentStatusMock.mockImplementation(() => { - return RiskEngineStatusEnum.DISABLED; - }); - const error = new Error('Risk engine is disabled or deleted already.'); - const mockTaskManagerStart = taskManagerMock.createStart(); + const error = new Error('testError'); mockSavedObjectClient.find.mockResolvedValueOnce(getSavedObjectConfiguration()); const riskScoreDataClient = riskScoreDataClientMock.create(); riskScoreDataClient.tearDown.mockResolvedValueOnce([error]); 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 80f5366cbe75d..6b8b0073594ee 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 @@ -252,21 +252,16 @@ export class RiskEngineDataClient { public async tearDown({ taskManager, riskScoreDataClient }: TearDownParams) { const errors: Error[] = []; const addError = (e: Error) => errors.push(e); - const riskEngineStatus = await this.getCurrentStatus(); - - if (riskEngineStatus === RiskEngineStatusEnum.ENABLED) { - await removeRiskScoringTask({ - namespace: this.options.namespace, - taskManager, - logger: this.options.logger, - }).catch(addError); + + await removeRiskScoringTask({ + namespace: this.options.namespace, + taskManager, + logger: this.options.logger, + }).catch(addError); - await deleteSavedObjects({ savedObjectsClient: this.options.soClient }).catch(addError); - const riskScoreErrors = await riskScoreDataClient.tearDown(); - errors.concat(riskScoreErrors); - } else { - errors.push(Error('Risk engine is disabled or deleted already.')); - } + await deleteSavedObjects({ savedObjectsClient: this.options.soClient }).catch(addError); + const riskScoreErrors = await riskScoreDataClient.tearDown(); + errors.concat(riskScoreErrors); return errors; } From fe0f3f528773347f488aeca9f7f26c02241b454f Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Thu, 19 Sep 2024 11:55:41 +0530 Subject: [PATCH 16/25] Addressing review comments : Move to Public URL space and lowercase --- .../common/entity_analytics/risk_engine/constants.ts | 2 +- .../security_solution/public/entity_analytics/api/api.ts | 4 ++-- .../entity_analytics/risk_engine/risk_engine_data_client.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts index ba25bd677e06d..35e236abfef68 100644 --- a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts +++ b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts @@ -12,11 +12,11 @@ export const RISK_ENGINE_ENABLE_URL = `${RISK_ENGINE_URL}/enable` as const; export const RISK_ENGINE_DISABLE_URL = `${RISK_ENGINE_URL}/disable` as const; export const RISK_ENGINE_PRIVILEGES_URL = `${RISK_ENGINE_URL}/privileges` as const; export const RISK_ENGINE_SETTINGS_URL = `${RISK_ENGINE_URL}/settings` as const; -export const RISK_ENGINE_CLEANUP_URL = `/api/risk_score/engine/dangerously_delete_data` as const; // Public Risk Score routes export const PUBLIC_RISK_ENGINE_URL = `${PUBLIC_RISK_SCORE_URL}/engine` as const; export const RISK_ENGINE_SCHEDULE_NOW_URL = `${RISK_ENGINE_URL}/schedule_now` as const; +export const RISK_ENGINE_CLEANUP_URL = `${PUBLIC_RISK_ENGINE_URL}/dangerously_delete_data` as const; export const MAX_SPACES_COUNT = 1; 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 7a0e4149bbd1a..ff9f27ae674ac 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 @@ -253,7 +253,7 @@ export const useEntityAnalyticsRoutes = () => { * Deletes Risk engine installation and associated data */ - const CleanUpRiskEngine = () => + const cleanUpRiskEngine = () => http.fetch(RISK_ENGINE_CLEANUP_URL, { version: '1', method: 'DELETE', @@ -275,7 +275,7 @@ export const useEntityAnalyticsRoutes = () => { getRiskScoreIndexStatus, fetchRiskEngineSettings, calculateEntityRiskScore, - CleanUpRiskEngine, + cleanUpRiskEngine, }; }, [http]); }; 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 6b8b0073594ee..36d2434bf7f91 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 @@ -252,7 +252,7 @@ export class RiskEngineDataClient { public async tearDown({ taskManager, riskScoreDataClient }: TearDownParams) { const errors: Error[] = []; const addError = (e: Error) => errors.push(e); - + await removeRiskScoringTask({ namespace: this.options.namespace, taskManager, From 1ed207820f83b3321740abbd326dea48f978a637 Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Thu, 19 Sep 2024 14:36:44 +0530 Subject: [PATCH 17/25] Reverting the risk_engine_data_client.ts to original version --- .../entity_analytics/risk_engine/risk_engine_data_client.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 36d2434bf7f91..c520e3fa07763 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 @@ -261,9 +261,8 @@ export class RiskEngineDataClient { await deleteSavedObjects({ savedObjectsClient: this.options.soClient }).catch(addError); const riskScoreErrors = await riskScoreDataClient.tearDown(); - errors.concat(riskScoreErrors); - return errors; + return errors.concat(riskScoreErrors); } public async disableLegacyRiskEngine({ namespace }: { namespace: string }) { From 303ca7df395be50764ccb7fb1b55ee2aa5a9aa00 Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Thu, 19 Sep 2024 15:46:59 +0530 Subject: [PATCH 18/25] Adding files after running openapi-:bundle --- ...entity_analytics_api_1.bundled.schema.yaml | 88 +++++++++++++++++++ ...entity_analytics_api_1.bundled.schema.yaml | 88 +++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_1.bundled.schema.yaml create mode 100644 x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_1.bundled.schema.yaml diff --git a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_1.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_1.bundled.schema.yaml new file mode 100644 index 0000000000000..def9522337146 --- /dev/null +++ b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_1.bundled.schema.yaml @@ -0,0 +1,88 @@ +openapi: 3.0.3 +info: + description: '' + title: Security Solution Entity Analytics API (Elastic Cloud and self-hosted) + version: '1' +servers: + - url: 'http://{kibana_host}:{port}' + variables: + kibana_host: + default: localhost + port: + default: '5601' +paths: + /api/risk_score/engine/dangerously_delete_data: + delete: + description: >- + Cleaning up the the Risk Engine by removing the indices, mapping and + transforms + operationId: CleanUpRiskEngine + responses: + '200': + content: + application/json: + schema: + type: object + properties: + cleanup_successful: + type: boolean + description: Successful response + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/TaskManagerUnavailableResponse' + description: Task manager is unavailable + default: + content: + application/json: + schema: + $ref: '#/components/schemas/CleanUpRiskEngineErrorResponse' + description: Unexpected error + summary: Cleanup the Risk Engine + tags: + - Security Solution Entity Analytics API +components: + schemas: + CleanUpRiskEngineErrorResponse: + type: object + properties: + cleanup_successful: + example: false + type: boolean + errors: + items: + type: object + properties: + error: + type: string + seq: + type: integer + required: + - seq + - error + type: array + required: + - cleanup_successful + - errors + TaskManagerUnavailableResponse: + description: Task manager is unavailable + type: object + properties: + message: + type: string + status_code: + minimum: 400 + type: integer + required: + - status_code + - message + securitySchemes: + BasicAuth: + scheme: basic + type: http +security: + - BasicAuth: [] +tags: + - description: '' + name: Security Solution Entity Analytics API diff --git a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_1.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_1.bundled.schema.yaml new file mode 100644 index 0000000000000..575884b58b9ad --- /dev/null +++ b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_1.bundled.schema.yaml @@ -0,0 +1,88 @@ +openapi: 3.0.3 +info: + description: '' + title: Security Solution Entity Analytics API (Elastic Cloud Serverless) + version: '1' +servers: + - url: 'http://{kibana_host}:{port}' + variables: + kibana_host: + default: localhost + port: + default: '5601' +paths: + /api/risk_score/engine/dangerously_delete_data: + delete: + description: >- + Cleaning up the the Risk Engine by removing the indices, mapping and + transforms + operationId: CleanUpRiskEngine + responses: + '200': + content: + application/json: + schema: + type: object + properties: + cleanup_successful: + type: boolean + description: Successful response + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/TaskManagerUnavailableResponse' + description: Task manager is unavailable + default: + content: + application/json: + schema: + $ref: '#/components/schemas/CleanUpRiskEngineErrorResponse' + description: Unexpected error + summary: Cleanup the Risk Engine + tags: + - Security Solution Entity Analytics API +components: + schemas: + CleanUpRiskEngineErrorResponse: + type: object + properties: + cleanup_successful: + example: false + type: boolean + errors: + items: + type: object + properties: + error: + type: string + seq: + type: integer + required: + - seq + - error + type: array + required: + - cleanup_successful + - errors + TaskManagerUnavailableResponse: + description: Task manager is unavailable + type: object + properties: + message: + type: string + status_code: + minimum: 400 + type: integer + required: + - status_code + - message + securitySchemes: + BasicAuth: + scheme: basic + type: http +security: + - BasicAuth: [] +tags: + - description: '' + name: Security Solution Entity Analytics API From d263a5e9077bbef68bfac5f5892d412158d6f66d Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Sun, 22 Sep 2024 19:01:33 +0530 Subject: [PATCH 19/25] Adding E2E integration test for the new endpoint --- .../common/api/quickstart_client.gen.ts | 5 +- .../public/entity_analytics/api/api.ts | 3 - .../trial_license_complete_tier/index.ts | 1 + .../risk_engine_cleanup_api.ts | 74 +++++++++++++++++++ .../entity_analytics/utils/risk_engine.ts | 4 +- 5 files changed, 79 insertions(+), 8 deletions(-) create mode 100644 x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_engine_cleanup_api.ts diff --git a/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts b/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts index c0e48082d7daf..177db51f3d26b 100644 --- a/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts +++ b/x-pack/plugins/security_solution/common/api/quickstart_client.gen.ts @@ -270,14 +270,11 @@ import type { StopEntityStoreRequestParamsInput, StopEntityStoreResponse, } from './entity_analytics/entity_store/engine/stop.gen'; -<<<<<<< ea-66-api-delete-risk-engine-installation-data -import type { CleanUpRiskEngineResponse } from './entity_analytics/risk_engine/engine_cleanup_route.gen'; -======= import type { ListEntitiesRequestQueryInput, ListEntitiesResponse, } from './entity_analytics/entity_store/entities/list_entities.gen'; ->>>>>>> main +import type { CleanUpRiskEngineResponse } from './entity_analytics/risk_engine/engine_cleanup_route.gen'; import type { DisableRiskEngineResponse } from './entity_analytics/risk_engine/engine_disable_route.gen'; import type { EnableRiskEngineResponse } from './entity_analytics/risk_engine/engine_enable_route.gen'; import type { InitRiskEngineResponse } from './entity_analytics/risk_engine/engine_init_route.gen'; 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 b369208d6bc95..18cb9ef570bd5 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 @@ -302,11 +302,8 @@ export const useEntityAnalyticsRoutes = () => { getRiskScoreIndexStatus, fetchRiskEngineSettings, calculateEntityRiskScore, -<<<<<<< ea-66-api-delete-risk-engine-installation-data cleanUpRiskEngine, -======= fetchEntitiesList, ->>>>>>> main }; }, [http]); }; diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/index.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/index.ts index 4879cce14f3a6..2aa04a898a449 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/index.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/index.ts @@ -10,6 +10,7 @@ import { FtrProviderContext } from '../../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('Entity Analytics - Risk Engine', function () { loadTestFile(require.resolve('./init_and_status_apis')); + loadTestFile(require.resolve('./risk_engine_cleanup_api')); loadTestFile(require.resolve('./risk_score_preview')); loadTestFile(require.resolve('./risk_scoring_task/task_execution')); loadTestFile(require.resolve('./risk_scoring_task/task_execution_nondefault_spaces')); diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_engine_cleanup_api.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_engine_cleanup_api.ts new file mode 100644 index 0000000000000..00905dd09f0e3 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_engine_cleanup_api.ts @@ -0,0 +1,74 @@ +/* + * 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 { v4 as uuidv4 } from 'uuid'; +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../../../ftr_provider_context'; +import { + buildDocument, + riskEngineRouteHelpersFactory, + waitForRiskScoresToBePresent, + createAndSyncRuleAndAlertsFactory, +} from '../../utils'; +import { dataGeneratorFactory } from '../../../detections_response/utils'; + +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const riskEngineRoutes = riskEngineRouteHelpersFactory(supertest); + const es = getService('es'); + const log = getService('log'); + const esArchiver = getService('esArchiver'); + + describe('@ess @ serverless @serverless QA risk_engine_cleanup_api', () => { + const createAndSyncRuleAndAlerts = createAndSyncRuleAndAlertsFactory({ supertest, log }); + const { indexListOfDocuments } = dataGeneratorFactory({ + es, + index: 'ecs_compliant', + log, + }); + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/security_solution/ecs_compliant'); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/security_solution/ecs_compliant'); + }); + + it('should return response with success status', async () => { + const status1 = await riskEngineRoutes.getStatus(); + expect(status1.body).to.eql({ + risk_engine_status: 'NOT_INSTALLED', + legacy_risk_engine_status: 'NOT_INSTALLED', + is_max_amount_of_risk_engines_reached: false, + }); + + const firstDocumentId = uuidv4(); + await indexListOfDocuments([buildDocument({ host: { name: 'host-1' } }, firstDocumentId)]); + await createAndSyncRuleAndAlerts({ query: `id: ${firstDocumentId}` }); + + await riskEngineRoutes.init(); + await waitForRiskScoresToBePresent({ es, log, scoreCount: 1 }); + + const status2 = await riskEngineRoutes.getStatus(); + expect(status2.body.risk_engine_status).to.be('ENABLED'); + expect(status2.body.legacy_risk_engine_status).to.be('NOT_INSTALLED'); + expect(status2.body.is_max_amount_of_risk_engines_reached).to.be(true); + + const response = await riskEngineRoutes.delete(); + expect(response.body).to.eql({ + cleanup_successful: true, + }); + + const status3 = await riskEngineRoutes.getStatus(); + expect(status3.body).to.eql({ + risk_engine_status: 'NOT_INSTALLED', + legacy_risk_engine_status: 'NOT_INSTALLED', + is_max_amount_of_risk_engines_reached: false, + }); + }); + }); +}; 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 e7c09a7ca8841..977ab1b3675f9 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 @@ -579,7 +579,9 @@ export const riskEngineRouteHelpersFactory = (supertest: SuperTest.Agent, namesp await supertest .delete(routeWithNamespace(RISK_ENGINE_CLEANUP_URL, namespace)) .set('kbn-xsrf', 'true') - .set('elastic-api-version', '1'), + .set('elastic-api-version', '2023-10-31') + .send() + .expect(expectStatusCode), scheduleNow: async (expectStatusCode: number = 200) => await supertest From 578dedbc75b64af3019bd0fcd502b3dda953561b Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Mon, 23 Sep 2024 12:44:07 +0530 Subject: [PATCH 20/25] Adding openapi:bundle changes (although unrelated to this PRs changes) --- ...entity_analytics_api_1.bundled.schema.yaml | 10 ++++ ...alytics_api_2023_10_31.bundled.schema.yaml | 52 ++++++++++++++++--- ...entity_analytics_api_1.bundled.schema.yaml | 10 ++++ ...alytics_api_2023_10_31.bundled.schema.yaml | 52 ++++++++++++++++--- 4 files changed, 108 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_1.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_1.bundled.schema.yaml index def9522337146..b2e74e008d622 100644 --- a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_1.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_1.bundled.schema.yaml @@ -39,10 +39,19 @@ paths: schema: $ref: '#/components/schemas/CleanUpRiskEngineErrorResponse' description: Unexpected error + servers: ! '' + security: ! '' summary: Cleanup the Risk Engine tags: - Security Solution Entity Analytics API components: + callbacks: ! '' + examples: ! '' + headers: ! '' + links: ! '' + parameters: ! '' + requestBodies: ! '' + responses: ! '' schemas: CleanUpRiskEngineErrorResponse: type: object @@ -86,3 +95,4 @@ security: tags: - description: '' name: Security Solution Entity Analytics API +externalDocs: ! '' diff --git a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml index c72b38d04c6cd..dad58176dc6d8 100644 --- a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml @@ -4,7 +4,7 @@ info: title: Security Solution Entity Analytics API (Elastic Cloud and self-hosted) version: '2023-10-31' servers: - - url: http://{kibana_host}:{port} + - url: 'http://{kibana_host}:{port}' variables: kibana_host: default: localhost @@ -57,6 +57,8 @@ paths: description: Successful response '400': description: Invalid request + servers: ! '' + security: ! '' summary: Delete an asset criticality record tags: - Security Solution Entity Analytics API @@ -88,6 +90,8 @@ paths: description: Invalid request '404': description: Criticality record not found + servers: ! '' + security: ! '' summary: Get an asset criticality record tags: - Security Solution Entity Analytics API @@ -125,6 +129,8 @@ paths: description: Successful response '400': description: Invalid request + servers: ! '' + security: ! '' summary: Upsert an asset criticality record tags: - Security Solution Entity Analytics API @@ -188,12 +194,14 @@ paths: description: Bulk upload successful '413': description: File too large + servers: ! '' + security: ! '' summary: Bulk upsert asset criticality records tags: - Security Solution Entity Analytics API /api/asset_criticality/list: get: - description: List asset criticality records, paging, sorting and filtering as needed. + description: 'List asset criticality records, paging, sorting and filtering as needed.' operationId: FindAssetCriticalityRecords parameters: - description: The field to sort by. @@ -264,6 +272,8 @@ paths: - per_page - total description: Bulk upload successful + servers: ! '' + security: ! '' summary: List asset criticality records tags: - Security Solution Entity Analytics API @@ -284,10 +294,12 @@ paths: $ref: '#/components/schemas/EngineDescriptor' type: array description: Successful response + servers: ! '' + security: ! '' summary: List the Entity Store engines tags: - Security Solution Entity Analytics API - /api/entity_store/engines/{entityType}: + '/api/entity_store/engines/{entityType}': delete: operationId: DeleteEntityStore parameters: @@ -313,6 +325,8 @@ paths: deleted: type: boolean description: Successful response + servers: ! '' + security: ! '' summary: Delete the Entity Store engine tags: - Security Solution Entity Analytics API @@ -332,10 +346,12 @@ paths: schema: $ref: '#/components/schemas/EngineDescriptor' description: Successful response + servers: ! '' + security: ! '' summary: Get the Entity Store engine tags: - Security Solution Entity Analytics API - /api/entity_store/engines/{entityType}/init: + '/api/entity_store/engines/{entityType}/init': post: operationId: InitEntityStore parameters: @@ -364,10 +380,12 @@ paths: schema: $ref: '#/components/schemas/EngineDescriptor' description: Successful response + servers: ! '' + security: ! '' summary: Initialize the Entity Store tags: - Security Solution Entity Analytics API - /api/entity_store/engines/{entityType}/start: + '/api/entity_store/engines/{entityType}/start': post: operationId: StartEntityStore parameters: @@ -387,10 +405,12 @@ paths: started: type: boolean description: Successful response + servers: ! '' + security: ! '' summary: Start the Entity Store engine tags: - Security Solution Entity Analytics API - /api/entity_store/engines/{entityType}/stats: + '/api/entity_store/engines/{entityType}/stats': post: operationId: GetEntityStoreStats parameters: @@ -422,10 +442,12 @@ paths: type: $ref: '#/components/schemas/EntityType' description: Successful response + servers: ! '' + security: ! '' summary: Get the Entity Store engine stats tags: - Security Solution Entity Analytics API - /api/entity_store/engines/{entityType}/stop: + '/api/entity_store/engines/{entityType}/stop': post: operationId: StopEntityStore parameters: @@ -445,12 +467,14 @@ paths: stopped: type: boolean description: Successful response + servers: ! '' + security: ! '' summary: Stop the Entity Store engine tags: - Security Solution Entity Analytics API /api/entity_store/entities/list: get: - description: List entities records, paging, sorting and filtering as needed. + description: 'List entities records, paging, sorting and filtering as needed.' operationId: ListEntities parameters: - in: query @@ -521,6 +545,8 @@ paths: - per_page - total description: Entities returned successfully + servers: ! '' + security: ! '' summary: List Entity Store Entities tags: - Security Solution Entity Analytics API @@ -553,10 +579,19 @@ paths: schema: $ref: '#/components/schemas/RiskEngineScheduleNowErrorResponse' description: Unexpected error + servers: ! '' + security: ! '' summary: Run the risk scoring engine tags: - Security Solution Entity Analytics API components: + callbacks: ! '' + examples: ! '' + headers: ! '' + links: ! '' + parameters: ! '' + requestBodies: ! '' + responses: ! '' schemas: AssetCriticalityBulkUploadErrorItem: type: object @@ -852,3 +887,4 @@ security: tags: - description: '' name: Security Solution Entity Analytics API +externalDocs: ! '' diff --git a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_1.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_1.bundled.schema.yaml index 575884b58b9ad..afa5f30b465f0 100644 --- a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_1.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_1.bundled.schema.yaml @@ -39,10 +39,19 @@ paths: schema: $ref: '#/components/schemas/CleanUpRiskEngineErrorResponse' description: Unexpected error + servers: ! '' + security: ! '' summary: Cleanup the Risk Engine tags: - Security Solution Entity Analytics API components: + callbacks: ! '' + examples: ! '' + headers: ! '' + links: ! '' + parameters: ! '' + requestBodies: ! '' + responses: ! '' schemas: CleanUpRiskEngineErrorResponse: type: object @@ -86,3 +95,4 @@ security: tags: - description: '' name: Security Solution Entity Analytics API +externalDocs: ! '' diff --git a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml index 9df11c7eb9e10..fa4866d2e3532 100644 --- a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml @@ -4,7 +4,7 @@ info: title: Security Solution Entity Analytics API (Elastic Cloud Serverless) version: '2023-10-31' servers: - - url: http://{kibana_host}:{port} + - url: 'http://{kibana_host}:{port}' variables: kibana_host: default: localhost @@ -57,6 +57,8 @@ paths: description: Successful response '400': description: Invalid request + servers: ! '' + security: ! '' summary: Delete an asset criticality record tags: - Security Solution Entity Analytics API @@ -88,6 +90,8 @@ paths: description: Invalid request '404': description: Criticality record not found + servers: ! '' + security: ! '' summary: Get an asset criticality record tags: - Security Solution Entity Analytics API @@ -125,6 +129,8 @@ paths: description: Successful response '400': description: Invalid request + servers: ! '' + security: ! '' summary: Upsert an asset criticality record tags: - Security Solution Entity Analytics API @@ -188,12 +194,14 @@ paths: description: Bulk upload successful '413': description: File too large + servers: ! '' + security: ! '' summary: Bulk upsert asset criticality records tags: - Security Solution Entity Analytics API /api/asset_criticality/list: get: - description: List asset criticality records, paging, sorting and filtering as needed. + description: 'List asset criticality records, paging, sorting and filtering as needed.' operationId: FindAssetCriticalityRecords parameters: - description: The field to sort by. @@ -264,6 +272,8 @@ paths: - per_page - total description: Bulk upload successful + servers: ! '' + security: ! '' summary: List asset criticality records tags: - Security Solution Entity Analytics API @@ -284,10 +294,12 @@ paths: $ref: '#/components/schemas/EngineDescriptor' type: array description: Successful response + servers: ! '' + security: ! '' summary: List the Entity Store engines tags: - Security Solution Entity Analytics API - /api/entity_store/engines/{entityType}: + '/api/entity_store/engines/{entityType}': delete: operationId: DeleteEntityStore parameters: @@ -313,6 +325,8 @@ paths: deleted: type: boolean description: Successful response + servers: ! '' + security: ! '' summary: Delete the Entity Store engine tags: - Security Solution Entity Analytics API @@ -332,10 +346,12 @@ paths: schema: $ref: '#/components/schemas/EngineDescriptor' description: Successful response + servers: ! '' + security: ! '' summary: Get the Entity Store engine tags: - Security Solution Entity Analytics API - /api/entity_store/engines/{entityType}/init: + '/api/entity_store/engines/{entityType}/init': post: operationId: InitEntityStore parameters: @@ -364,10 +380,12 @@ paths: schema: $ref: '#/components/schemas/EngineDescriptor' description: Successful response + servers: ! '' + security: ! '' summary: Initialize the Entity Store tags: - Security Solution Entity Analytics API - /api/entity_store/engines/{entityType}/start: + '/api/entity_store/engines/{entityType}/start': post: operationId: StartEntityStore parameters: @@ -387,10 +405,12 @@ paths: started: type: boolean description: Successful response + servers: ! '' + security: ! '' summary: Start the Entity Store engine tags: - Security Solution Entity Analytics API - /api/entity_store/engines/{entityType}/stats: + '/api/entity_store/engines/{entityType}/stats': post: operationId: GetEntityStoreStats parameters: @@ -422,10 +442,12 @@ paths: type: $ref: '#/components/schemas/EntityType' description: Successful response + servers: ! '' + security: ! '' summary: Get the Entity Store engine stats tags: - Security Solution Entity Analytics API - /api/entity_store/engines/{entityType}/stop: + '/api/entity_store/engines/{entityType}/stop': post: operationId: StopEntityStore parameters: @@ -445,12 +467,14 @@ paths: stopped: type: boolean description: Successful response + servers: ! '' + security: ! '' summary: Stop the Entity Store engine tags: - Security Solution Entity Analytics API /api/entity_store/entities/list: get: - description: List entities records, paging, sorting and filtering as needed. + description: 'List entities records, paging, sorting and filtering as needed.' operationId: ListEntities parameters: - in: query @@ -521,6 +545,8 @@ paths: - per_page - total description: Entities returned successfully + servers: ! '' + security: ! '' summary: List Entity Store Entities tags: - Security Solution Entity Analytics API @@ -553,10 +579,19 @@ paths: schema: $ref: '#/components/schemas/RiskEngineScheduleNowErrorResponse' description: Unexpected error + servers: ! '' + security: ! '' summary: Run the risk scoring engine tags: - Security Solution Entity Analytics API components: + callbacks: ! '' + examples: ! '' + headers: ! '' + links: ! '' + parameters: ! '' + requestBodies: ! '' + responses: ! '' schemas: AssetCriticalityBulkUploadErrorItem: type: object @@ -852,3 +887,4 @@ security: tags: - description: '' name: Security Solution Entity Analytics API +externalDocs: ! '' From 6f422ab67cbf0e406dc49b8461fbcb5079c63588 Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Mon, 23 Sep 2024 21:02:41 +0530 Subject: [PATCH 21/25] Openapi:bundle changes --- ...entity_analytics_api_1.bundled.schema.yaml | 12 +---- ...alytics_api_2023_10_31.bundled.schema.yaml | 52 +++---------------- ...entity_analytics_api_1.bundled.schema.yaml | 12 +---- ...alytics_api_2023_10_31.bundled.schema.yaml | 52 +++---------------- 4 files changed, 18 insertions(+), 110 deletions(-) diff --git a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_1.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_1.bundled.schema.yaml index b2e74e008d622..917097f77618e 100644 --- a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_1.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_1.bundled.schema.yaml @@ -4,7 +4,7 @@ info: title: Security Solution Entity Analytics API (Elastic Cloud and self-hosted) version: '1' servers: - - url: 'http://{kibana_host}:{port}' + - url: http://{kibana_host}:{port} variables: kibana_host: default: localhost @@ -39,19 +39,10 @@ paths: schema: $ref: '#/components/schemas/CleanUpRiskEngineErrorResponse' description: Unexpected error - servers: ! '' - security: ! '' summary: Cleanup the Risk Engine tags: - Security Solution Entity Analytics API components: - callbacks: ! '' - examples: ! '' - headers: ! '' - links: ! '' - parameters: ! '' - requestBodies: ! '' - responses: ! '' schemas: CleanUpRiskEngineErrorResponse: type: object @@ -95,4 +86,3 @@ security: tags: - description: '' name: Security Solution Entity Analytics API -externalDocs: ! '' diff --git a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml index dad58176dc6d8..c72b38d04c6cd 100644 --- a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml @@ -4,7 +4,7 @@ info: title: Security Solution Entity Analytics API (Elastic Cloud and self-hosted) version: '2023-10-31' servers: - - url: 'http://{kibana_host}:{port}' + - url: http://{kibana_host}:{port} variables: kibana_host: default: localhost @@ -57,8 +57,6 @@ paths: description: Successful response '400': description: Invalid request - servers: ! '' - security: ! '' summary: Delete an asset criticality record tags: - Security Solution Entity Analytics API @@ -90,8 +88,6 @@ paths: description: Invalid request '404': description: Criticality record not found - servers: ! '' - security: ! '' summary: Get an asset criticality record tags: - Security Solution Entity Analytics API @@ -129,8 +125,6 @@ paths: description: Successful response '400': description: Invalid request - servers: ! '' - security: ! '' summary: Upsert an asset criticality record tags: - Security Solution Entity Analytics API @@ -194,14 +188,12 @@ paths: description: Bulk upload successful '413': description: File too large - servers: ! '' - security: ! '' summary: Bulk upsert asset criticality records tags: - Security Solution Entity Analytics API /api/asset_criticality/list: get: - description: 'List asset criticality records, paging, sorting and filtering as needed.' + description: List asset criticality records, paging, sorting and filtering as needed. operationId: FindAssetCriticalityRecords parameters: - description: The field to sort by. @@ -272,8 +264,6 @@ paths: - per_page - total description: Bulk upload successful - servers: ! '' - security: ! '' summary: List asset criticality records tags: - Security Solution Entity Analytics API @@ -294,12 +284,10 @@ paths: $ref: '#/components/schemas/EngineDescriptor' type: array description: Successful response - servers: ! '' - security: ! '' summary: List the Entity Store engines tags: - Security Solution Entity Analytics API - '/api/entity_store/engines/{entityType}': + /api/entity_store/engines/{entityType}: delete: operationId: DeleteEntityStore parameters: @@ -325,8 +313,6 @@ paths: deleted: type: boolean description: Successful response - servers: ! '' - security: ! '' summary: Delete the Entity Store engine tags: - Security Solution Entity Analytics API @@ -346,12 +332,10 @@ paths: schema: $ref: '#/components/schemas/EngineDescriptor' description: Successful response - servers: ! '' - security: ! '' summary: Get the Entity Store engine tags: - Security Solution Entity Analytics API - '/api/entity_store/engines/{entityType}/init': + /api/entity_store/engines/{entityType}/init: post: operationId: InitEntityStore parameters: @@ -380,12 +364,10 @@ paths: schema: $ref: '#/components/schemas/EngineDescriptor' description: Successful response - servers: ! '' - security: ! '' summary: Initialize the Entity Store tags: - Security Solution Entity Analytics API - '/api/entity_store/engines/{entityType}/start': + /api/entity_store/engines/{entityType}/start: post: operationId: StartEntityStore parameters: @@ -405,12 +387,10 @@ paths: started: type: boolean description: Successful response - servers: ! '' - security: ! '' summary: Start the Entity Store engine tags: - Security Solution Entity Analytics API - '/api/entity_store/engines/{entityType}/stats': + /api/entity_store/engines/{entityType}/stats: post: operationId: GetEntityStoreStats parameters: @@ -442,12 +422,10 @@ paths: type: $ref: '#/components/schemas/EntityType' description: Successful response - servers: ! '' - security: ! '' summary: Get the Entity Store engine stats tags: - Security Solution Entity Analytics API - '/api/entity_store/engines/{entityType}/stop': + /api/entity_store/engines/{entityType}/stop: post: operationId: StopEntityStore parameters: @@ -467,14 +445,12 @@ paths: stopped: type: boolean description: Successful response - servers: ! '' - security: ! '' summary: Stop the Entity Store engine tags: - Security Solution Entity Analytics API /api/entity_store/entities/list: get: - description: 'List entities records, paging, sorting and filtering as needed.' + description: List entities records, paging, sorting and filtering as needed. operationId: ListEntities parameters: - in: query @@ -545,8 +521,6 @@ paths: - per_page - total description: Entities returned successfully - servers: ! '' - security: ! '' summary: List Entity Store Entities tags: - Security Solution Entity Analytics API @@ -579,19 +553,10 @@ paths: schema: $ref: '#/components/schemas/RiskEngineScheduleNowErrorResponse' description: Unexpected error - servers: ! '' - security: ! '' summary: Run the risk scoring engine tags: - Security Solution Entity Analytics API components: - callbacks: ! '' - examples: ! '' - headers: ! '' - links: ! '' - parameters: ! '' - requestBodies: ! '' - responses: ! '' schemas: AssetCriticalityBulkUploadErrorItem: type: object @@ -887,4 +852,3 @@ security: tags: - description: '' name: Security Solution Entity Analytics API -externalDocs: ! '' diff --git a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_1.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_1.bundled.schema.yaml index afa5f30b465f0..c25c2f19c5456 100644 --- a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_1.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_1.bundled.schema.yaml @@ -4,7 +4,7 @@ info: title: Security Solution Entity Analytics API (Elastic Cloud Serverless) version: '1' servers: - - url: 'http://{kibana_host}:{port}' + - url: http://{kibana_host}:{port} variables: kibana_host: default: localhost @@ -39,19 +39,10 @@ paths: schema: $ref: '#/components/schemas/CleanUpRiskEngineErrorResponse' description: Unexpected error - servers: ! '' - security: ! '' summary: Cleanup the Risk Engine tags: - Security Solution Entity Analytics API components: - callbacks: ! '' - examples: ! '' - headers: ! '' - links: ! '' - parameters: ! '' - requestBodies: ! '' - responses: ! '' schemas: CleanUpRiskEngineErrorResponse: type: object @@ -95,4 +86,3 @@ security: tags: - description: '' name: Security Solution Entity Analytics API -externalDocs: ! '' diff --git a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml index fa4866d2e3532..9df11c7eb9e10 100644 --- a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_2023_10_31.bundled.schema.yaml @@ -4,7 +4,7 @@ info: title: Security Solution Entity Analytics API (Elastic Cloud Serverless) version: '2023-10-31' servers: - - url: 'http://{kibana_host}:{port}' + - url: http://{kibana_host}:{port} variables: kibana_host: default: localhost @@ -57,8 +57,6 @@ paths: description: Successful response '400': description: Invalid request - servers: ! '' - security: ! '' summary: Delete an asset criticality record tags: - Security Solution Entity Analytics API @@ -90,8 +88,6 @@ paths: description: Invalid request '404': description: Criticality record not found - servers: ! '' - security: ! '' summary: Get an asset criticality record tags: - Security Solution Entity Analytics API @@ -129,8 +125,6 @@ paths: description: Successful response '400': description: Invalid request - servers: ! '' - security: ! '' summary: Upsert an asset criticality record tags: - Security Solution Entity Analytics API @@ -194,14 +188,12 @@ paths: description: Bulk upload successful '413': description: File too large - servers: ! '' - security: ! '' summary: Bulk upsert asset criticality records tags: - Security Solution Entity Analytics API /api/asset_criticality/list: get: - description: 'List asset criticality records, paging, sorting and filtering as needed.' + description: List asset criticality records, paging, sorting and filtering as needed. operationId: FindAssetCriticalityRecords parameters: - description: The field to sort by. @@ -272,8 +264,6 @@ paths: - per_page - total description: Bulk upload successful - servers: ! '' - security: ! '' summary: List asset criticality records tags: - Security Solution Entity Analytics API @@ -294,12 +284,10 @@ paths: $ref: '#/components/schemas/EngineDescriptor' type: array description: Successful response - servers: ! '' - security: ! '' summary: List the Entity Store engines tags: - Security Solution Entity Analytics API - '/api/entity_store/engines/{entityType}': + /api/entity_store/engines/{entityType}: delete: operationId: DeleteEntityStore parameters: @@ -325,8 +313,6 @@ paths: deleted: type: boolean description: Successful response - servers: ! '' - security: ! '' summary: Delete the Entity Store engine tags: - Security Solution Entity Analytics API @@ -346,12 +332,10 @@ paths: schema: $ref: '#/components/schemas/EngineDescriptor' description: Successful response - servers: ! '' - security: ! '' summary: Get the Entity Store engine tags: - Security Solution Entity Analytics API - '/api/entity_store/engines/{entityType}/init': + /api/entity_store/engines/{entityType}/init: post: operationId: InitEntityStore parameters: @@ -380,12 +364,10 @@ paths: schema: $ref: '#/components/schemas/EngineDescriptor' description: Successful response - servers: ! '' - security: ! '' summary: Initialize the Entity Store tags: - Security Solution Entity Analytics API - '/api/entity_store/engines/{entityType}/start': + /api/entity_store/engines/{entityType}/start: post: operationId: StartEntityStore parameters: @@ -405,12 +387,10 @@ paths: started: type: boolean description: Successful response - servers: ! '' - security: ! '' summary: Start the Entity Store engine tags: - Security Solution Entity Analytics API - '/api/entity_store/engines/{entityType}/stats': + /api/entity_store/engines/{entityType}/stats: post: operationId: GetEntityStoreStats parameters: @@ -442,12 +422,10 @@ paths: type: $ref: '#/components/schemas/EntityType' description: Successful response - servers: ! '' - security: ! '' summary: Get the Entity Store engine stats tags: - Security Solution Entity Analytics API - '/api/entity_store/engines/{entityType}/stop': + /api/entity_store/engines/{entityType}/stop: post: operationId: StopEntityStore parameters: @@ -467,14 +445,12 @@ paths: stopped: type: boolean description: Successful response - servers: ! '' - security: ! '' summary: Stop the Entity Store engine tags: - Security Solution Entity Analytics API /api/entity_store/entities/list: get: - description: 'List entities records, paging, sorting and filtering as needed.' + description: List entities records, paging, sorting and filtering as needed. operationId: ListEntities parameters: - in: query @@ -545,8 +521,6 @@ paths: - per_page - total description: Entities returned successfully - servers: ! '' - security: ! '' summary: List Entity Store Entities tags: - Security Solution Entity Analytics API @@ -579,19 +553,10 @@ paths: schema: $ref: '#/components/schemas/RiskEngineScheduleNowErrorResponse' description: Unexpected error - servers: ! '' - security: ! '' summary: Run the risk scoring engine tags: - Security Solution Entity Analytics API components: - callbacks: ! '' - examples: ! '' - headers: ! '' - links: ! '' - parameters: ! '' - requestBodies: ! '' - responses: ! '' schemas: AssetCriticalityBulkUploadErrorItem: type: object @@ -887,4 +852,3 @@ security: tags: - description: '' name: Security Solution Entity Analytics API -externalDocs: ! '' From 8cb4de75c48da8ea55f4c76eccf02668fbf9b209 Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Tue, 24 Sep 2024 13:20:07 +0530 Subject: [PATCH 22/25] Changes after 'make api-docs && make api-docs-staging' --- .../output/kibana.serverless.staging.yaml | 54 +++++++++++++++++++ oas_docs/output/kibana.staging.yaml | 54 +++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/oas_docs/output/kibana.serverless.staging.yaml b/oas_docs/output/kibana.serverless.staging.yaml index 15790040e6a46..30e6e17e41a36 100644 --- a/oas_docs/output/kibana.serverless.staging.yaml +++ b/oas_docs/output/kibana.serverless.staging.yaml @@ -15607,6 +15607,39 @@ paths: tags: - Security Solution Timeline API - access:securitySolution + /api/risk_score/engine/dangerously_delete_data: + delete: + description: >- + Cleaning up the the Risk Engine by removing the indices, mapping and + transforms + operationId: CleanUpRiskEngine + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + type: object + properties: + cleanup_successful: + type: boolean + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: >- + #/components/schemas/Security_Solution_Entity_Analytics_API_TaskManagerUnavailableResponse + description: Task manager is unavailable + default: + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: >- + #/components/schemas/Security_Solution_Entity_Analytics_API_CleanUpRiskEngineErrorResponse + description: Unexpected error + summary: Cleanup the Risk Engine + tags: + - Security Solution Entity Analytics API /api/risk_score/engine/schedule_now: post: description: >- @@ -30995,6 +31028,27 @@ components: required: - id_value - id_field + Security_Solution_Entity_Analytics_API_CleanUpRiskEngineErrorResponse: + type: object + properties: + cleanup_successful: + example: false + type: boolean + errors: + items: + type: object + properties: + error: + type: string + seq: + type: integer + required: + - seq + - error + type: array + required: + - cleanup_successful + - errors Security_Solution_Entity_Analytics_API_CreateAssetCriticalityRecord: allOf: - $ref: >- diff --git a/oas_docs/output/kibana.staging.yaml b/oas_docs/output/kibana.staging.yaml index c2b530c0af263..1d1ba2b5aaaad 100644 --- a/oas_docs/output/kibana.staging.yaml +++ b/oas_docs/output/kibana.staging.yaml @@ -19056,6 +19056,39 @@ paths: tags: - Security Solution Timeline API - access:securitySolution + /api/risk_score/engine/dangerously_delete_data: + delete: + description: >- + Cleaning up the the Risk Engine by removing the indices, mapping and + transforms + operationId: CleanUpRiskEngine + responses: + '200': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + type: object + properties: + cleanup_successful: + type: boolean + description: Successful response + '400': + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: >- + #/components/schemas/Security_Solution_Entity_Analytics_API_TaskManagerUnavailableResponse + description: Task manager is unavailable + default: + content: + application/json; Elastic-Api-Version=2023-10-31: + schema: + $ref: >- + #/components/schemas/Security_Solution_Entity_Analytics_API_CleanUpRiskEngineErrorResponse + description: Unexpected error + summary: Cleanup the Risk Engine + tags: + - Security Solution Entity Analytics API /api/risk_score/engine/schedule_now: post: description: >- @@ -39024,6 +39057,27 @@ components: required: - id_value - id_field + Security_Solution_Entity_Analytics_API_CleanUpRiskEngineErrorResponse: + type: object + properties: + cleanup_successful: + example: false + type: boolean + errors: + items: + type: object + properties: + error: + type: string + seq: + type: integer + required: + - seq + - error + type: array + required: + - cleanup_successful + - errors Security_Solution_Entity_Analytics_API_CreateAssetCriticalityRecord: allOf: - $ref: >- From 46e81b36959eedf211dba2f1eb2c70f1453785cf Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Tue, 24 Sep 2024 21:09:27 +0530 Subject: [PATCH 23/25] Changes to integration test since the key is_max_amount_of_risk_engines_reached is removed and merged before --- .../risk_engine_cleanup_api.ts | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_engine_cleanup_api.ts b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_engine_cleanup_api.ts index 00905dd09f0e3..48344403093b3 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_engine_cleanup_api.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/entity_analytics/risk_engine/trial_license_complete_tier/risk_engine_cleanup_api.ts @@ -40,11 +40,8 @@ export default ({ getService }: FtrProviderContext) => { it('should return response with success status', async () => { const status1 = await riskEngineRoutes.getStatus(); - expect(status1.body).to.eql({ - risk_engine_status: 'NOT_INSTALLED', - legacy_risk_engine_status: 'NOT_INSTALLED', - is_max_amount_of_risk_engines_reached: false, - }); + expect(status1.body.risk_engine_status).to.be('NOT_INSTALLED'); + expect(status1.body.legacy_risk_engine_status).to.be('NOT_INSTALLED'); const firstDocumentId = uuidv4(); await indexListOfDocuments([buildDocument({ host: { name: 'host-1' } }, firstDocumentId)]); @@ -56,7 +53,6 @@ export default ({ getService }: FtrProviderContext) => { const status2 = await riskEngineRoutes.getStatus(); expect(status2.body.risk_engine_status).to.be('ENABLED'); expect(status2.body.legacy_risk_engine_status).to.be('NOT_INSTALLED'); - expect(status2.body.is_max_amount_of_risk_engines_reached).to.be(true); const response = await riskEngineRoutes.delete(); expect(response.body).to.eql({ @@ -64,11 +60,8 @@ export default ({ getService }: FtrProviderContext) => { }); const status3 = await riskEngineRoutes.getStatus(); - expect(status3.body).to.eql({ - risk_engine_status: 'NOT_INSTALLED', - legacy_risk_engine_status: 'NOT_INSTALLED', - is_max_amount_of_risk_engines_reached: false, - }); + expect(status3.body.risk_engine_status).to.be('NOT_INSTALLED'); + expect(status3.body.legacy_risk_engine_status).to.be('NOT_INSTALLED'); }); }); }; From b647467b1933cf274fce322629140209e99d4149 Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Wed, 25 Sep 2024 12:04:54 +0530 Subject: [PATCH 24/25] Adding changes for openapi:bundle --- ...rity_solution_entity_analytics_api_1.bundled.schema.yaml | 6 +++--- ...rity_solution_entity_analytics_api_1.bundled.schema.yaml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_1.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_1.bundled.schema.yaml index 917097f77618e..9d6d57abd382a 100644 --- a/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_1.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/ess/security_solution_entity_analytics_api_1.bundled.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.3 info: description: '' - title: Security Solution Entity Analytics API (Elastic Cloud and self-hosted) + title: Security Entity Analytics API (Elastic Cloud and self-hosted) version: '1' servers: - url: http://{kibana_host}:{port} @@ -41,7 +41,7 @@ paths: description: Unexpected error summary: Cleanup the Risk Engine tags: - - Security Solution Entity Analytics API + - Security Entity Analytics API components: schemas: CleanUpRiskEngineErrorResponse: @@ -85,4 +85,4 @@ security: - BasicAuth: [] tags: - description: '' - name: Security Solution Entity Analytics API + name: Security Entity Analytics API diff --git a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_1.bundled.schema.yaml b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_1.bundled.schema.yaml index c25c2f19c5456..835d8f79b1fea 100644 --- a/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_1.bundled.schema.yaml +++ b/x-pack/plugins/security_solution/docs/openapi/serverless/security_solution_entity_analytics_api_1.bundled.schema.yaml @@ -1,7 +1,7 @@ openapi: 3.0.3 info: description: '' - title: Security Solution Entity Analytics API (Elastic Cloud Serverless) + title: Security Entity Analytics API (Elastic Cloud Serverless) version: '1' servers: - url: http://{kibana_host}:{port} @@ -41,7 +41,7 @@ paths: description: Unexpected error summary: Cleanup the Risk Engine tags: - - Security Solution Entity Analytics API + - Security Entity Analytics API components: schemas: CleanUpRiskEngineErrorResponse: @@ -85,4 +85,4 @@ security: - BasicAuth: [] tags: - description: '' - name: Security Solution Entity Analytics API + name: Security Entity Analytics API From 498d5bbc49b7118cbfe5a1dfc533a59f5321d7bb Mon Sep 17 00:00:00 2001 From: abhishekbhatia1710 Date: Wed, 25 Sep 2024 13:08:48 +0530 Subject: [PATCH 25/25] Changes after make api-docs and make api-docs-staging --- oas_docs/output/kibana.serverless.staging.yaml | 10 +++++----- oas_docs/output/kibana.staging.yaml | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/oas_docs/output/kibana.serverless.staging.yaml b/oas_docs/output/kibana.serverless.staging.yaml index 5e51000fa0aac..58abfc3e1f7f9 100644 --- a/oas_docs/output/kibana.serverless.staging.yaml +++ b/oas_docs/output/kibana.serverless.staging.yaml @@ -15413,18 +15413,18 @@ paths: application/json; Elastic-Api-Version=2023-10-31: schema: $ref: >- - #/components/schemas/Security_Solution_Entity_Analytics_API_TaskManagerUnavailableResponse + #/components/schemas/Security_Entity_Analytics_API_TaskManagerUnavailableResponse description: Task manager is unavailable default: content: application/json; Elastic-Api-Version=2023-10-31: schema: $ref: >- - #/components/schemas/Security_Solution_Entity_Analytics_API_CleanUpRiskEngineErrorResponse + #/components/schemas/Security_Entity_Analytics_API_CleanUpRiskEngineErrorResponse description: Unexpected error summary: Cleanup the Risk Engine tags: - - Security Solution Entity Analytics API + - Security Entity Analytics API /api/risk_score/engine/schedule_now: post: description: >- @@ -29783,7 +29783,7 @@ components: required: - id_value - id_field - Security_Solution_Entity_Analytics_API_CleanUpRiskEngineErrorResponse: + Security_Entity_Analytics_API_CleanUpRiskEngineErrorResponse: type: object properties: cleanup_successful: @@ -29804,7 +29804,7 @@ components: required: - cleanup_successful - errors - Security_Solution_Entity_Analytics_API_CreateAssetCriticalityRecord: + Security_Entity_Analytics_API_CreateAssetCriticalityRecord: allOf: - $ref: >- #/components/schemas/Security_Entity_Analytics_API_AssetCriticalityRecordIdParts diff --git a/oas_docs/output/kibana.staging.yaml b/oas_docs/output/kibana.staging.yaml index c7d410f71cc25..59a22166f3498 100644 --- a/oas_docs/output/kibana.staging.yaml +++ b/oas_docs/output/kibana.staging.yaml @@ -18843,18 +18843,18 @@ paths: application/json; Elastic-Api-Version=2023-10-31: schema: $ref: >- - #/components/schemas/Security_Solution_Entity_Analytics_API_TaskManagerUnavailableResponse + #/components/schemas/Security_Entity_Analytics_API_TaskManagerUnavailableResponse description: Task manager is unavailable default: content: application/json; Elastic-Api-Version=2023-10-31: schema: $ref: >- - #/components/schemas/Security_Solution_Entity_Analytics_API_CleanUpRiskEngineErrorResponse + #/components/schemas/Security_Entity_Analytics_API_CleanUpRiskEngineErrorResponse description: Unexpected error summary: Cleanup the Risk Engine tags: - - Security Solution Entity Analytics API + - Security Entity Analytics API /api/risk_score/engine/schedule_now: post: description: >- @@ -37792,7 +37792,7 @@ components: required: - id_value - id_field - Security_Solution_Entity_Analytics_API_CleanUpRiskEngineErrorResponse: + Security_Entity_Analytics_API_CleanUpRiskEngineErrorResponse: type: object properties: cleanup_successful: @@ -37813,7 +37813,7 @@ components: required: - cleanup_successful - errors - Security_Solution_Entity_Analytics_API_CreateAssetCriticalityRecord: + Security_Entity_Analytics_API_CreateAssetCriticalityRecord: allOf: - $ref: >- #/components/schemas/Security_Entity_Analytics_API_AssetCriticalityRecordIdParts