diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index 690765f3d0c4d..c52407a864c31 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -328,6 +328,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { detectionsReq: `${SECURITY_SOLUTION_DOCS}detections-permissions-section.html`, networkMap: `${SECURITY_SOLUTION_DOCS}conf-map-ui.html`, troubleshootGaps: `${SECURITY_SOLUTION_DOCS}alerts-ui-monitor.html#troubleshoot-gaps`, + ruleApiOverview: `${SECURITY_SOLUTION_DOCS}rule-api-overview.html`, }, securitySolution: { trustedApps: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/trusted-apps-ov.html`, diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index 5e85d01c22ce2..f017c2ec4be00 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -233,6 +233,7 @@ export interface DocLinks { readonly detectionsReq: string; readonly networkMap: string; readonly troubleshootGaps: string; + readonly ruleApiOverview: string; }; readonly securitySolution: { readonly trustedApps: string; diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 3b820b16ef8a4..74f9bff078b89 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -235,6 +235,12 @@ export const DETECTION_ENGINE_PREPACKAGED_RULES_STATUS_URL = export const DETECTION_ENGINE_RULES_BULK_ACTION = `${DETECTION_ENGINE_RULES_URL}/_bulk_action` as const; export const DETECTION_ENGINE_RULES_PREVIEW = `${DETECTION_ENGINE_RULES_URL}/preview` as const; +export const DETECTION_ENGINE_RULES_BULK_DELETE = + `${DETECTION_ENGINE_RULES_URL}/_bulk_delete` as const; +export const DETECTION_ENGINE_RULES_BULK_CREATE = + `${DETECTION_ENGINE_RULES_URL}/_bulk_create` as const; +export const DETECTION_ENGINE_RULES_BULK_UPDATE = + `${DETECTION_ENGINE_RULES_URL}/_bulk_update` as const; /** * Internal detection engine routes diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts index 94c4de459a2ea..e38df7657f512 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts @@ -24,6 +24,9 @@ import { DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL, DETECTION_ENGINE_RULES_BULK_ACTION, DETECTION_ENGINE_RULE_EXECUTION_EVENTS_URL, + DETECTION_ENGINE_RULES_BULK_UPDATE, + DETECTION_ENGINE_RULES_BULK_DELETE, + DETECTION_ENGINE_RULES_BULK_CREATE, } from '../../../../../common/constants'; import { GetAggregateRuleExecutionEventsResponse } from '../../../../../common/detection_engine/schemas/response'; import { RuleAlertType, HapiReadableStream } from '../../rules/types'; @@ -110,21 +113,21 @@ export const getFindRequest = () => export const getReadBulkRequest = () => requestMock.create({ method: 'post', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_create`, + path: DETECTION_ENGINE_RULES_BULK_CREATE, body: [getCreateRulesSchemaMock()], }); export const getUpdateBulkRequest = () => requestMock.create({ method: 'put', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, + path: DETECTION_ENGINE_RULES_BULK_UPDATE, body: [getCreateRulesSchemaMock()], }); export const getPatchBulkRequest = () => requestMock.create({ method: 'patch', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, + path: DETECTION_ENGINE_RULES_BULK_UPDATE, body: [getCreateRulesSchemaMock()], }); @@ -145,28 +148,28 @@ export const getBulkActionEditRequest = () => export const getDeleteBulkRequest = () => requestMock.create({ method: 'delete', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_delete`, + path: DETECTION_ENGINE_RULES_BULK_DELETE, body: [{ rule_id: 'rule-1' }], }); export const getDeleteBulkRequestById = () => requestMock.create({ method: 'delete', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_delete`, + path: DETECTION_ENGINE_RULES_BULK_DELETE, body: [{ id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd' }], }); export const getDeleteAsPostBulkRequestById = () => requestMock.create({ method: 'post', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_delete`, + path: DETECTION_ENGINE_RULES_BULK_DELETE, body: [{ id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd' }], }); export const getDeleteAsPostBulkRequest = () => requestMock.create({ method: 'post', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_delete`, + path: DETECTION_ENGINE_RULES_BULK_DELETE, body: [{ rule_id: 'rule-1' }], }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts index 68a3ec0733b60..1d63a977e2480 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; +import { DETECTION_ENGINE_RULES_BULK_CREATE } from '../../../../../common/constants'; import { mlServicesMock, mlAuthzMock as mockMlAuthzFactory } from '../../../machine_learning/mocks'; import { buildMlAuthz } from '../../../machine_learning/authz'; import { @@ -23,6 +23,7 @@ import { getCreateRulesSchemaMock } from '../../../../../common/detection_engine // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { elasticsearchClientMock } from 'src/core/server/elasticsearch/client/mocks'; import { getQueryRuleParams } from '../../schemas/rule_schemas.mock'; +import { loggingSystemMock } from '../../../../../../../../src/core/server/mocks'; jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create()); @@ -38,6 +39,7 @@ describe.each([ server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); ml = mlServicesMock.createSetupContract(); + const logger = loggingSystemMock.createLogger(); clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); // no existing rules clients.rulesClient.create.mockResolvedValue( @@ -47,7 +49,7 @@ describe.each([ context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue( elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse()) ); - createRulesBulkRoute(server.router, ml, isRuleRegistryEnabled); + createRulesBulkRoute(server.router, ml, isRuleRegistryEnabled, logger); }); describe('status codes', () => { @@ -137,7 +139,7 @@ describe.each([ test('returns an error object if duplicate rule_ids found in request payload', async () => { const request = requestMock.create({ method: 'post', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_create`, + path: DETECTION_ENGINE_RULES_BULK_CREATE, body: [getCreateRulesSchemaMock(), getCreateRulesSchemaMock()], }); const response = await server.inject(request, context); @@ -158,7 +160,7 @@ describe.each([ test('allows rule type of query', async () => { const request = requestMock.create({ method: 'post', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_create`, + path: DETECTION_ENGINE_RULES_BULK_CREATE, body: [{ ...getCreateRulesSchemaMock(), type: 'query' }], }); const result = server.validate(request); @@ -169,7 +171,7 @@ describe.each([ test('allows rule type of query and custom from and interval', async () => { const request = requestMock.create({ method: 'post', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_create`, + path: DETECTION_ENGINE_RULES_BULK_CREATE, body: [{ from: 'now-7m', interval: '5m', ...getCreateRulesSchemaMock() }], }); const result = server.validate(request); @@ -180,7 +182,7 @@ describe.each([ test('disallows unknown rule type', async () => { const request = requestMock.create({ method: 'post', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_create`, + path: DETECTION_ENGINE_RULES_BULK_CREATE, body: [{ ...getCreateRulesSchemaMock(), type: 'unexpected_type' }], }); const result = server.validate(request); @@ -191,7 +193,7 @@ describe.each([ test('disallows invalid "from" param on rule', async () => { const request = requestMock.create({ method: 'post', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_create`, + path: DETECTION_ENGINE_RULES_BULK_CREATE, body: [ { from: 'now-3755555555555555.67s', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts index 74f777b29ca01..8a350e7e12f46 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts @@ -12,7 +12,7 @@ import { createRulesBulkSchema } from '../../../../../common/detection_engine/sc import { rulesBulkSchema } from '../../../../../common/detection_engine/schemas/response/rules_bulk_schema'; import type { SecuritySolutionPluginRouter } from '../../../../types'; import { - DETECTION_ENGINE_RULES_URL, + DETECTION_ENGINE_RULES_BULK_CREATE, NOTIFICATION_THROTTLE_NO_ACTIONS, } from '../../../../../common/constants'; import { SetupPlugins } from '../../../../plugin'; @@ -25,15 +25,21 @@ import { buildRouteValidation } from '../../../../utils/build_validation/route_v import { transformBulkError, createBulkErrorObject, buildSiemResponse } from '../utils'; import { convertCreateAPIToInternalSchema } from '../../schemas/rule_converters'; +import { getDeprecatedBulkEndpointHeader, logDeprecatedBulkEndpoint } from './utils/deprecation'; +import { Logger } from '../../../../../../../../src/core/server'; +/** + * @deprecated since version 8.2.0. Use the detection_engine/rules/_bulk_action API instead + */ export const createRulesBulkRoute = ( router: SecuritySolutionPluginRouter, ml: SetupPlugins['ml'], - isRuleRegistryEnabled: boolean + isRuleRegistryEnabled: boolean, + logger: Logger ) => { router.post( { - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_create`, + path: DETECTION_ENGINE_RULES_BULK_CREATE, validate: { body: buildRouteValidation(createRulesBulkSchema), }, @@ -42,6 +48,8 @@ export const createRulesBulkRoute = ( }, }, async (context, request, response) => { + logDeprecatedBulkEndpoint(logger, DETECTION_ENGINE_RULES_BULK_CREATE); + const siemResponse = buildSiemResponse(response); const rulesClient = context.alerting.getRulesClient(); const esClient = context.core.elasticsearch.client; @@ -138,9 +146,16 @@ export const createRulesBulkRoute = ( ]; const [validated, errors] = validate(rulesBulk, rulesBulkSchema); if (errors != null) { - return siemResponse.error({ statusCode: 500, body: errors }); + return siemResponse.error({ + statusCode: 500, + body: errors, + headers: getDeprecatedBulkEndpointHeader(DETECTION_ENGINE_RULES_BULK_CREATE), + }); } else { - return response.ok({ body: validated ?? {} }); + return response.ok({ + body: validated ?? {}, + headers: getDeprecatedBulkEndpointHeader(DETECTION_ENGINE_RULES_BULK_CREATE), + }); } } ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts index 4ac4822c412fa..9d46ebabb7c8a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; +import { DETECTION_ENGINE_RULES_BULK_DELETE } from '../../../../../common/constants'; import { getEmptyFindResult, getFindResultWithSingleHit, @@ -17,6 +17,7 @@ import { } from '../__mocks__/request_responses'; import { requestContextMock, serverMock, requestMock } from '../__mocks__'; import { deleteRulesBulkRoute } from './delete_rules_bulk_route'; +import { loggingSystemMock } from '../../../../../../../../src/core/server/mocks'; describe.each([ ['Legacy', false], @@ -28,12 +29,13 @@ describe.each([ beforeEach(() => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); + const logger = loggingSystemMock.createLogger(); clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); // rule exists clients.rulesClient.delete.mockResolvedValue({}); // successful deletion clients.savedObjectsClient.find.mockResolvedValue(getEmptySavedObjectsResponse()); // rule status request - deleteRulesBulkRoute(server.router, isRuleRegistryEnabled); + deleteRulesBulkRoute(server.router, isRuleRegistryEnabled, logger); }); describe('status codes with actionClient and alertClient', () => { @@ -42,7 +44,7 @@ describe.each([ expect(response.status).toEqual(200); }); - test('resturns 200 when deleting a single rule and related rule status', async () => { + test('returns 200 when deleting a single rule and related rule status', async () => { const response = await server.inject(getDeleteBulkRequest(), context); expect(response.status).toEqual(200); }); @@ -88,7 +90,7 @@ describe.each([ test('rejects requests without IDs', async () => { const request = requestMock.create({ method: 'post', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_delete`, + path: DETECTION_ENGINE_RULES_BULK_DELETE, body: [{}], }); const response = await server.inject(request, context); @@ -104,7 +106,7 @@ describe.each([ test('rejects requests with both id and rule_id', async () => { const request = requestMock.create({ method: 'post', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_delete`, + path: DETECTION_ENGINE_RULES_BULK_DELETE, body: [{ id: 'c1e1b359-7ac1-4e96-bc81-c683c092436f', rule_id: 'rule_1' }], }); const response = await server.inject(request, context); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts index 442b707532ea6..cd3c219675ccb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/delete_rules_bulk_route.ts @@ -14,18 +14,19 @@ import { QueryRulesBulkSchemaDecoded, } from '../../../../../common/detection_engine/schemas/request/query_rules_bulk_schema'; import { rulesBulkSchema } from '../../../../../common/detection_engine/schemas/response/rules_bulk_schema'; -import type { RouteConfig, RequestHandler } from '../../../../../../../../src/core/server'; +import type { RouteConfig, RequestHandler, Logger } from '../../../../../../../../src/core/server'; import type { SecuritySolutionPluginRouter, SecuritySolutionRequestHandlerContext, } from '../../../../types'; -import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; +import { DETECTION_ENGINE_RULES_BULK_DELETE } from '../../../../../common/constants'; import { getIdBulkError } from './utils'; import { transformValidateBulkError } from './validate'; import { transformBulkError, buildSiemResponse, createBulkErrorObject } from '../utils'; import { deleteRules } from '../../rules/delete_rules'; import { readRules } from '../../rules/read_rules'; import { legacyMigrate } from '../../rules/utils'; +import { getDeprecatedBulkEndpointHeader, logDeprecatedBulkEndpoint } from './utils/deprecation'; type Config = RouteConfig; type Handler = RequestHandler< @@ -36,9 +37,13 @@ type Handler = RequestHandler< 'delete' | 'post' >; +/** + * @deprecated since version 8.2.0. Use the detection_engine/rules/_bulk_action API instead + */ export const deleteRulesBulkRoute = ( router: SecuritySolutionPluginRouter, - isRuleRegistryEnabled: boolean + isRuleRegistryEnabled: boolean, + logger: Logger ) => { const config: Config = { validate: { @@ -46,12 +51,14 @@ export const deleteRulesBulkRoute = ( queryRulesBulkSchema ), }, - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_delete`, + path: DETECTION_ENGINE_RULES_BULK_DELETE, options: { tags: ['access:securitySolution'], }, }; const handler: Handler = async (context, request, response) => { + logDeprecatedBulkEndpoint(logger, DETECTION_ENGINE_RULES_BULK_DELETE); + const siemResponse = buildSiemResponse(response); const rulesClient = context.alerting.getRulesClient(); const ruleExecutionLog = context.securitySolution.getRuleExecutionLog(); @@ -102,9 +109,16 @@ export const deleteRulesBulkRoute = ( ); const [validated, errors] = validate(rules, rulesBulkSchema); if (errors != null) { - return siemResponse.error({ statusCode: 500, body: errors }); + return siemResponse.error({ + statusCode: 500, + body: errors, + headers: getDeprecatedBulkEndpointHeader(DETECTION_ENGINE_RULES_BULK_DELETE), + }); } else { - return response.ok({ body: validated ?? {} }); + return response.ok({ + body: validated ?? {}, + headers: getDeprecatedBulkEndpointHeader(DETECTION_ENGINE_RULES_BULK_DELETE), + }); } }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.test.ts index 6b3fa7ad83c68..9539ad2cb9c7b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.test.ts @@ -5,7 +5,10 @@ * 2.0. */ -import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; +import { + DETECTION_ENGINE_RULES_BULK_UPDATE, + DETECTION_ENGINE_RULES_URL, +} from '../../../../../common/constants'; import { mlServicesMock, mlAuthzMock as mockMlAuthzFactory } from '../../../machine_learning/mocks'; import { buildMlAuthz } from '../../../machine_learning/authz'; import { @@ -19,6 +22,7 @@ import { serverMock, requestContextMock, requestMock } from '../__mocks__'; import { patchRulesBulkRoute } from './patch_rules_bulk_route'; import { getCreateRulesSchemaMock } from '../../../../../common/detection_engine/schemas/request/rule_schemas.mock'; import { getQueryRuleParams } from '../../schemas/rule_schemas.mock'; +import { loggingSystemMock } from '../../../../../../../../src/core/server/mocks'; jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create()); @@ -34,13 +38,14 @@ describe.each([ server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); ml = mlServicesMock.createSetupContract(); + const logger = loggingSystemMock.createLogger(); clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); // rule exists clients.rulesClient.update.mockResolvedValue( getAlertMock(isRuleRegistryEnabled, getQueryRuleParams()) ); // update succeeds - patchRulesBulkRoute(server.router, ml, isRuleRegistryEnabled); + patchRulesBulkRoute(server.router, ml, isRuleRegistryEnabled, logger); }); describe('status codes', () => { @@ -96,7 +101,7 @@ describe.each([ }); const request = requestMock.create({ method: 'patch', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, + path: DETECTION_ENGINE_RULES_BULK_UPDATE, body: [typicalMlRulePayload()], }); const response = await server.inject(request, context); @@ -122,7 +127,7 @@ describe.each([ const { type, ...payloadWithoutType } = typicalMlRulePayload(); const request = requestMock.create({ method: 'patch', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, + path: DETECTION_ENGINE_RULES_BULK_UPDATE, body: [payloadWithoutType], }); const response = await server.inject(request, context); @@ -144,7 +149,7 @@ describe.each([ test('rejects payloads with no ID', async () => { const request = requestMock.create({ method: 'patch', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, + path: DETECTION_ENGINE_RULES_BULK_UPDATE, body: [{ ...getCreateRulesSchemaMock(), rule_id: undefined }], }); const response = await server.inject(request, context); @@ -164,7 +169,7 @@ describe.each([ test('allows query rule type', async () => { const request = requestMock.create({ method: 'patch', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, + path: DETECTION_ENGINE_RULES_BULK_UPDATE, body: [{ ...getCreateRulesSchemaMock(), type: 'query' }], }); const result = server.validate(request); @@ -175,7 +180,7 @@ describe.each([ test('rejects unknown rule type', async () => { const request = requestMock.create({ method: 'patch', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, + path: DETECTION_ENGINE_RULES_BULK_UPDATE, body: [{ ...getCreateRulesSchemaMock(), type: 'unknown_type' }], }); const result = server.validate(request); @@ -188,7 +193,7 @@ describe.each([ test('allows rule type of query and custom from and interval', async () => { const request = requestMock.create({ method: 'patch', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, + path: DETECTION_ENGINE_RULES_BULK_UPDATE, body: [{ from: 'now-7m', interval: '5m', ...getCreateRulesSchemaMock() }], }); const result = server.validate(request); @@ -199,7 +204,7 @@ describe.each([ test('disallows invalid "from" param on rule', async () => { const request = requestMock.create({ method: 'patch', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, + path: DETECTION_ENGINE_RULES_BULK_UPDATE, body: [ { from: 'now-3755555555555555.67s', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts index 58d364cb34b5c..aedb78a248c34 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts @@ -14,7 +14,7 @@ import { import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; import { rulesBulkSchema } from '../../../../../common/detection_engine/schemas/response/rules_bulk_schema'; import type { SecuritySolutionPluginRouter } from '../../../../types'; -import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; +import { DETECTION_ENGINE_RULES_BULK_UPDATE } from '../../../../../common/constants'; import { SetupPlugins } from '../../../../plugin'; import { buildMlAuthz } from '../../../machine_learning/authz'; import { throwAuthzError } from '../../../machine_learning/validation'; @@ -25,15 +25,21 @@ import { patchRules } from '../../rules/patch_rules'; import { readRules } from '../../rules/read_rules'; import { PartialFilter } from '../../types'; import { legacyMigrate } from '../../rules/utils'; +import { getDeprecatedBulkEndpointHeader, logDeprecatedBulkEndpoint } from './utils/deprecation'; +import { Logger } from '../../../../../../../../src/core/server'; +/** + * @deprecated since version 8.2.0. Use the detection_engine/rules/_bulk_action API instead + */ export const patchRulesBulkRoute = ( router: SecuritySolutionPluginRouter, ml: SetupPlugins['ml'], - isRuleRegistryEnabled: boolean + isRuleRegistryEnabled: boolean, + logger: Logger ) => { router.patch( { - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, + path: DETECTION_ENGINE_RULES_BULK_UPDATE, validate: { body: buildRouteValidation( patchRulesBulkSchema @@ -44,6 +50,8 @@ export const patchRulesBulkRoute = ( }, }, async (context, request, response) => { + logDeprecatedBulkEndpoint(logger, DETECTION_ENGINE_RULES_BULK_UPDATE); + const siemResponse = buildSiemResponse(response); const rulesClient = context.alerting.getRulesClient(); @@ -207,9 +215,16 @@ export const patchRulesBulkRoute = ( const [validated, errors] = validate(rules, rulesBulkSchema); if (errors != null) { - return siemResponse.error({ statusCode: 500, body: errors }); + return siemResponse.error({ + statusCode: 500, + body: errors, + headers: getDeprecatedBulkEndpointHeader(DETECTION_ENGINE_RULES_BULK_UPDATE), + }); } else { - return response.ok({ body: validated ?? {} }); + return response.ok({ + body: validated ?? {}, + headers: getDeprecatedBulkEndpointHeader(DETECTION_ENGINE_RULES_BULK_UPDATE), + }); } } ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts index 88c15f99ed6f7..12b7968b9793a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; +import { DETECTION_ENGINE_RULES_BULK_UPDATE } from '../../../../../common/constants'; import { mlServicesMock, mlAuthzMock as mockMlAuthzFactory } from '../../../machine_learning/mocks'; import { buildMlAuthz } from '../../../machine_learning/authz'; import { @@ -20,6 +20,7 @@ import { updateRulesBulkRoute } from './update_rules_bulk_route'; import { BulkError } from '../utils'; import { getCreateRulesSchemaMock } from '../../../../../common/detection_engine/schemas/request/rule_schemas.mock'; import { getQueryRuleParams } from '../../schemas/rule_schemas.mock'; +import { loggingSystemMock } from '../../../../../../../../src/core/server/mocks'; jest.mock('../../../machine_learning/authz', () => mockMlAuthzFactory.create()); @@ -35,6 +36,7 @@ describe.each([ server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); ml = mlServicesMock.createSetupContract(); + const logger = loggingSystemMock.createLogger(); clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit(isRuleRegistryEnabled)); clients.rulesClient.update.mockResolvedValue( @@ -43,7 +45,7 @@ describe.each([ clients.appClient.getSignalsIndex.mockReturnValue('.siem-signals-test-index'); - updateRulesBulkRoute(server.router, ml, isRuleRegistryEnabled); + updateRulesBulkRoute(server.router, ml, isRuleRegistryEnabled, logger); }); describe('status codes', () => { @@ -90,7 +92,7 @@ describe.each([ }); const request = requestMock.create({ method: 'put', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, + path: DETECTION_ENGINE_RULES_BULK_UPDATE, body: [typicalMlRulePayload()], }); @@ -112,7 +114,7 @@ describe.each([ test('rejects payloads with no ID', async () => { const noIdRequest = requestMock.create({ method: 'put', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, + path: DETECTION_ENGINE_RULES_BULK_UPDATE, body: [{ ...getCreateRulesSchemaMock(), rule_id: undefined }], }); const response = await server.inject(noIdRequest, context); @@ -127,7 +129,7 @@ describe.each([ test('allows query rule type', async () => { const request = requestMock.create({ method: 'put', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, + path: DETECTION_ENGINE_RULES_BULK_UPDATE, body: [{ ...getCreateRulesSchemaMock(), type: 'query' }], }); const result = server.validate(request); @@ -138,7 +140,7 @@ describe.each([ test('rejects unknown rule type', async () => { const request = requestMock.create({ method: 'put', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, + path: DETECTION_ENGINE_RULES_BULK_UPDATE, body: [{ ...getCreateRulesSchemaMock(), type: 'unknown_type' }], }); const result = server.validate(request); @@ -149,7 +151,7 @@ describe.each([ test('allows rule type of query and custom from and interval', async () => { const request = requestMock.create({ method: 'put', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, + path: DETECTION_ENGINE_RULES_BULK_UPDATE, body: [{ from: 'now-7m', interval: '5m', ...getCreateRulesSchemaMock(), type: 'query' }], }); const result = server.validate(request); @@ -160,7 +162,7 @@ describe.each([ test('disallows invalid "from" param on rule', async () => { const request = requestMock.create({ method: 'put', - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, + path: DETECTION_ENGINE_RULES_BULK_UPDATE, body: [ { from: 'now-3755555555555555.67s', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts index d1df5713914df..646fab5077dec 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/update_rules_bulk_route.ts @@ -11,7 +11,7 @@ import { buildRouteValidation } from '../../../../utils/build_validation/route_v import { updateRulesBulkSchema } from '../../../../../common/detection_engine/schemas/request/update_rules_bulk_schema'; import { rulesBulkSchema } from '../../../../../common/detection_engine/schemas/response/rules_bulk_schema'; import type { SecuritySolutionPluginRouter } from '../../../../types'; -import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; +import { DETECTION_ENGINE_RULES_BULK_UPDATE } from '../../../../../common/constants'; import { SetupPlugins } from '../../../../plugin'; import { buildMlAuthz } from '../../../machine_learning/authz'; import { throwAuthzError } from '../../../machine_learning/validation'; @@ -21,15 +21,21 @@ import { transformBulkError, buildSiemResponse, createBulkErrorObject } from '.. import { updateRules } from '../../rules/update_rules'; import { legacyMigrate } from '../../rules/utils'; import { readRules } from '../../rules/read_rules'; +import { getDeprecatedBulkEndpointHeader, logDeprecatedBulkEndpoint } from './utils/deprecation'; +import { Logger } from '../../../../../../../../src/core/server'; +/** + * @deprecated since version 8.2.0. Use the detection_engine/rules/_bulk_action API instead + */ export const updateRulesBulkRoute = ( router: SecuritySolutionPluginRouter, ml: SetupPlugins['ml'], - isRuleRegistryEnabled: boolean + isRuleRegistryEnabled: boolean, + logger: Logger ) => { router.put( { - path: `${DETECTION_ENGINE_RULES_URL}/_bulk_update`, + path: DETECTION_ENGINE_RULES_BULK_UPDATE, validate: { body: buildRouteValidation(updateRulesBulkSchema), }, @@ -38,6 +44,8 @@ export const updateRulesBulkRoute = ( }, }, async (context, request, response) => { + logDeprecatedBulkEndpoint(logger, DETECTION_ENGINE_RULES_BULK_UPDATE); + const siemResponse = buildSiemResponse(response); const rulesClient = context.alerting.getRulesClient(); @@ -105,9 +113,16 @@ export const updateRulesBulkRoute = ( const [validated, errors] = validate(rules, rulesBulkSchema); if (errors != null) { - return siemResponse.error({ statusCode: 500, body: errors }); + return siemResponse.error({ + statusCode: 500, + body: errors, + headers: getDeprecatedBulkEndpointHeader(DETECTION_ENGINE_RULES_BULK_UPDATE), + }); } else { - return response.ok({ body: validated ?? {} }); + return response.ok({ + body: validated ?? {}, + headers: getDeprecatedBulkEndpointHeader(DETECTION_ENGINE_RULES_BULK_UPDATE), + }); } } ); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils/deprecation.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils/deprecation.ts new file mode 100644 index 0000000000000..18f77bfa85bc3 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/utils/deprecation.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getDocLinks } from '@kbn/doc-links'; +import { Logger } from 'src/core/server'; +import { DETECTION_ENGINE_RULES_BULK_ACTION } from '../../../../../../common/constants'; + +/** + * Helper method for building deprecation messages + * + * @param path Deprecated endpoint path + * @returns string + */ +export const buildDeprecatedBulkEndpointMessage = (path: string) => { + const docsLink = getDocLinks({ kibanaBranch: 'main' }).siem.ruleApiOverview; + return `Deprecated endpoint: ${path} API is deprecated since v8.2. Please use the ${DETECTION_ENGINE_RULES_BULK_ACTION} API instead. See ${docsLink} for more detail.`; +}; + +/** + * Logs usages of a deprecated bulk endpoint + * + * @param logger System logger + * @param path Deprecated endpoint path + */ +export const logDeprecatedBulkEndpoint = (logger: Logger, path: string) => { + logger.warn(buildDeprecatedBulkEndpointMessage(path), { tags: ['deprecation'] }); +}; + +/** + * Creates a warning header with a message formatted according to RFC7234. + * We follow the same formatting as Elasticsearch + * https://github.com/elastic/elasticsearch/blob/5baabff6670a8ed49297488ca8cac8ec12a2078d/server/src/main/java/org/elasticsearch/common/logging/HeaderWarning.java#L55 + * + * @param path Deprecated endpoint path + */ +export const getDeprecatedBulkEndpointHeader = (path: string) => ({ + warning: `299 Kibana "${buildDeprecatedBulkEndpointMessage(path)}"`, +}); diff --git a/x-pack/plugins/security_solution/server/routes/index.ts b/x-pack/plugins/security_solution/server/routes/index.ts index 2efb132c96ff6..8646cb54088c5 100644 --- a/x-pack/plugins/security_solution/server/routes/index.ts +++ b/x-pack/plugins/security_solution/server/routes/index.ts @@ -112,10 +112,10 @@ export const initRoutes = ( addPrepackedRulesRoute(router); getPrepackagedRulesStatusRoute(router, config, security, isRuleRegistryEnabled); - createRulesBulkRoute(router, ml, isRuleRegistryEnabled); - updateRulesBulkRoute(router, ml, isRuleRegistryEnabled); - patchRulesBulkRoute(router, ml, isRuleRegistryEnabled); - deleteRulesBulkRoute(router, isRuleRegistryEnabled); + createRulesBulkRoute(router, ml, isRuleRegistryEnabled, logger); + updateRulesBulkRoute(router, ml, isRuleRegistryEnabled, logger); + patchRulesBulkRoute(router, ml, isRuleRegistryEnabled, logger); + deleteRulesBulkRoute(router, isRuleRegistryEnabled, logger); performBulkActionRoute(router, ml, logger, isRuleRegistryEnabled); getRuleExecutionEventsRoute(router); diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/create_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/basic/tests/create_rules_bulk.ts index b54e1432f5463..bb450d8f0efdc 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/create_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/basic/tests/create_rules_bulk.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; -import { DETECTION_ENGINE_RULES_URL } from '../../../../plugins/security_solution/common/constants'; +import { DETECTION_ENGINE_RULES_BULK_CREATE } from '../../../../plugins/security_solution/common/constants'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createSignalsIndex, @@ -48,7 +48,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should create a single rule with a rule_id', async () => { const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_create`) + .post(DETECTION_ENGINE_RULES_BULK_CREATE) .set('kbn-xsrf', 'true') .send([getSimpleRule()]) .expect(200); @@ -59,7 +59,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should create a single rule without a rule_id', async () => { const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_create`) + .post(DETECTION_ENGINE_RULES_BULK_CREATE) .set('kbn-xsrf', 'true') .send([getSimpleRuleWithoutRuleId()]) .expect(200); @@ -70,7 +70,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should return a 200 ok but have a 409 conflict if we attempt to create the same rule_id twice', async () => { const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_create`) + .post(DETECTION_ENGINE_RULES_BULK_CREATE) .set('kbn-xsrf', 'true') .send([getSimpleRule(), getSimpleRule()]) .expect(200); @@ -88,13 +88,13 @@ export default ({ getService }: FtrProviderContext): void => { it('should return a 200 ok but have a 409 conflict if we attempt to create the same rule_id that already exists', async () => { await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_create`) + .post(DETECTION_ENGINE_RULES_BULK_CREATE) .set('kbn-xsrf', 'true') .send([getSimpleRule()]) .expect(200); const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_create`) + .post(DETECTION_ENGINE_RULES_BULK_CREATE) .set('kbn-xsrf', 'foo') .send([getSimpleRule()]) .expect(200); diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/delete_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/basic/tests/delete_rules_bulk.ts index b7517697ad2a9..09c2f5960fae5 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/delete_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/basic/tests/delete_rules_bulk.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; -import { DETECTION_ENGINE_RULES_URL } from '../../../../plugins/security_solution/common/constants'; +import { DETECTION_ENGINE_RULES_BULK_DELETE } from '../../../../plugins/security_solution/common/constants'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createRule, @@ -43,7 +43,7 @@ export default ({ getService }: FtrProviderContext): void => { // delete the rule in bulk const { body } = await supertest - .delete(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .delete(DETECTION_ENGINE_RULES_BULK_DELETE) .set('kbn-xsrf', 'true') .send([{ rule_id: 'rule-1' }]) .expect(200); @@ -57,7 +57,7 @@ export default ({ getService }: FtrProviderContext): void => { // delete that rule by its rule_id const { body } = await supertest - .delete(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .delete(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ rule_id: bodyWithCreatedRule.rule_id }]) .set('kbn-xsrf', 'true') .expect(200); @@ -71,7 +71,7 @@ export default ({ getService }: FtrProviderContext): void => { // delete that rule by its id const { body } = await supertest - .delete(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .delete(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ id: bodyWithCreatedRule.id }]) .set('kbn-xsrf', 'true') .expect(200); @@ -82,7 +82,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should return an error if the ruled_id does not exist when trying to delete a rule_id', async () => { const { body } = await supertest - .delete(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .delete(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ rule_id: 'fake_id' }]) .set('kbn-xsrf', 'true') .expect(200); @@ -100,7 +100,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should return an error if the id does not exist when trying to delete an id', async () => { const { body } = await supertest - .delete(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .delete(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ id: 'c4e80a0d-e20f-4efc-84c1-08112da5a612' }]) .set('kbn-xsrf', 'true') .expect(200); @@ -120,7 +120,7 @@ export default ({ getService }: FtrProviderContext): void => { const bodyWithCreatedRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); const { body } = await supertest - .delete(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .delete(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ id: bodyWithCreatedRule.id }, { id: 'c4e80a0d-e20f-4efc-84c1-08112da5a612' }]) .set('kbn-xsrf', 'true') .expect(200); @@ -155,7 +155,7 @@ export default ({ getService }: FtrProviderContext): void => { // delete the rule in bulk const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .post(DETECTION_ENGINE_RULES_BULK_DELETE) .set('kbn-xsrf', 'true') .send([{ rule_id: 'rule-1' }]) .expect(200); @@ -169,7 +169,7 @@ export default ({ getService }: FtrProviderContext): void => { // delete that rule by its rule_id const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .post(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ rule_id: bodyWithCreatedRule.rule_id }]) .set('kbn-xsrf', 'true') .expect(200); @@ -183,7 +183,7 @@ export default ({ getService }: FtrProviderContext): void => { // delete that rule by its id const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .post(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ id: bodyWithCreatedRule.id }]) .set('kbn-xsrf', 'true') .expect(200); @@ -194,7 +194,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should return an error if the ruled_id does not exist when trying to delete a rule_id', async () => { const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .post(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ rule_id: 'fake_id' }]) .set('kbn-xsrf', 'true') .expect(200); @@ -212,7 +212,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should return an error if the id does not exist when trying to delete an id', async () => { const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .post(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ id: 'c4e80a0d-e20f-4efc-84c1-08112da5a612' }]) .set('kbn-xsrf', 'true') .expect(200); @@ -232,7 +232,7 @@ export default ({ getService }: FtrProviderContext): void => { const bodyWithCreatedRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .post(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ id: bodyWithCreatedRule.id }, { id: 'c4e80a0d-e20f-4efc-84c1-08112da5a612' }]) .set('kbn-xsrf', 'true') .expect(200); diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/patch_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/basic/tests/patch_rules_bulk.ts index 0be23c1d8a289..dd1e36e4fb624 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/patch_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/basic/tests/patch_rules_bulk.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; -import { DETECTION_ENGINE_RULES_URL } from '../../../../plugins/security_solution/common/constants'; +import { DETECTION_ENGINE_RULES_BULK_UPDATE } from '../../../../plugins/security_solution/common/constants'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createSignalsIndex, @@ -42,7 +42,7 @@ export default ({ getService }: FtrProviderContext) => { // patch a simple rule's name const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([{ rule_id: 'rule-1', name: 'some other name' }]) .expect(200); @@ -60,7 +60,7 @@ export default ({ getService }: FtrProviderContext) => { // patch both rule names const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([ { rule_id: 'rule-1', name: 'some other name' }, @@ -87,7 +87,7 @@ export default ({ getService }: FtrProviderContext) => { // patch a simple rule's name const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([{ id: createRuleBody.id, name: 'some other name' }]) .expect(200); @@ -105,7 +105,7 @@ export default ({ getService }: FtrProviderContext) => { // patch both rule names const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([ { id: createRule1.id, name: 'some other name' }, @@ -132,7 +132,7 @@ export default ({ getService }: FtrProviderContext) => { // patch a simple rule's name const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([{ id: createdBody.id, name: 'some other name' }]) .expect(200); @@ -149,7 +149,7 @@ export default ({ getService }: FtrProviderContext) => { // patch a simple rule's enabled to false const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([{ rule_id: 'rule-1', enabled: false }]) .expect(200); @@ -166,7 +166,7 @@ export default ({ getService }: FtrProviderContext) => { // patch a simple rule's enabled to false and another property const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([{ rule_id: 'rule-1', severity: 'low', enabled: false }]) .expect(200); @@ -185,14 +185,14 @@ export default ({ getService }: FtrProviderContext) => { // patch a simple rule's timeline_title await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([{ rule_id: 'rule-1', timeline_title: 'some title', timeline_id: 'some id' }]) .expect(200); // patch a simple rule's name const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([{ rule_id: 'rule-1', name: 'some other name' }]) .expect(200); @@ -209,7 +209,7 @@ export default ({ getService }: FtrProviderContext) => { it('should return a 200 but give a 404 in the message if it is given a fake id', async () => { const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([{ id: '5096dec6-b6b9-4d8d-8f93-6c2602079d9d', name: 'some other name' }]) .expect(200); @@ -227,7 +227,7 @@ export default ({ getService }: FtrProviderContext) => { it('should return a 200 but give a 404 in the message if it is given a fake rule_id', async () => { const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([{ rule_id: 'fake_id', name: 'some other name' }]) .expect(200); @@ -245,7 +245,7 @@ export default ({ getService }: FtrProviderContext) => { // patch one rule name and give a fake id for the second const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([ { rule_id: 'rule-1', name: 'some other name' }, @@ -275,7 +275,7 @@ export default ({ getService }: FtrProviderContext) => { // patch one rule name and give a fake id for the second const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([ { id: createdBody.id, name: 'some other name' }, diff --git a/x-pack/test/detection_engine_api_integration/basic/tests/update_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/basic/tests/update_rules_bulk.ts index 46e34869a8e03..a2043c49cdbc2 100644 --- a/x-pack/test/detection_engine_api_integration/basic/tests/update_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/basic/tests/update_rules_bulk.ts @@ -7,7 +7,10 @@ import expect from '@kbn/expect'; -import { DETECTION_ENGINE_RULES_URL } from '../../../../plugins/security_solution/common/constants'; +import { + DETECTION_ENGINE_RULES_BULK_UPDATE, + DETECTION_ENGINE_RULES_URL, +} from '../../../../plugins/security_solution/common/constants'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createSignalsIndex, @@ -46,7 +49,7 @@ export default ({ getService }: FtrProviderContext) => { // update a simple rule's name const { body } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([updatedRule]) .expect(200); @@ -76,7 +79,7 @@ export default ({ getService }: FtrProviderContext) => { // update both rule names const { body } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([updatedRule1, updatedRule2]) .expect(200); @@ -105,7 +108,7 @@ export default ({ getService }: FtrProviderContext) => { delete updatedRule1.rule_id; const { body } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([updatedRule1]) .expect(200); @@ -133,7 +136,7 @@ export default ({ getService }: FtrProviderContext) => { delete updatedRule2.rule_id; const { body } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([updatedRule1, updatedRule2]) .expect(200); @@ -162,7 +165,7 @@ export default ({ getService }: FtrProviderContext) => { delete updatedRule1.rule_id; const { body } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([updatedRule1]) .expect(200); @@ -183,7 +186,7 @@ export default ({ getService }: FtrProviderContext) => { updatedRule1.enabled = false; const { body } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([updatedRule1]) .expect(200); @@ -206,7 +209,7 @@ export default ({ getService }: FtrProviderContext) => { ruleUpdate.timeline_id = 'some id'; await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([ruleUpdate]) .expect(200); @@ -216,7 +219,7 @@ export default ({ getService }: FtrProviderContext) => { ruleUpdate2.name = 'some other name'; const { body } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([ruleUpdate2]) .expect(200); @@ -235,7 +238,7 @@ export default ({ getService }: FtrProviderContext) => { delete ruleUpdate.rule_id; const { body } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([ruleUpdate]) .expect(200); @@ -257,7 +260,7 @@ export default ({ getService }: FtrProviderContext) => { delete ruleUpdate.id; const { body } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([ruleUpdate]) .expect(200); @@ -283,7 +286,7 @@ export default ({ getService }: FtrProviderContext) => { // update one rule name and give a fake id for the second const { body } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([ruleUpdate, ruleUpdate2]) .expect(200); @@ -320,7 +323,7 @@ export default ({ getService }: FtrProviderContext) => { rule2.name = 'some other name'; const { body } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([rule1, rule2]) .expect(200); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts index 7ea9e4cdb5f84..d2181d779e4e1 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_rules_bulk.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; -import { DETECTION_ENGINE_RULES_URL } from '../../../../plugins/security_solution/common/constants'; +import { DETECTION_ENGINE_RULES_BULK_CREATE } from '../../../../plugins/security_solution/common/constants'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createSignalsIndex, @@ -30,6 +30,24 @@ export default ({ getService }: FtrProviderContext): void => { const log = getService('log'); describe('create_rules_bulk', () => { + describe('deprecations', () => { + afterEach(async () => { + await deleteAllAlerts(supertest, log); + }); + + it('should return a warning header', async () => { + const { header } = await supertest + .post(DETECTION_ENGINE_RULES_BULK_CREATE) + .set('kbn-xsrf', 'true') + .send([getSimpleRule()]) + .expect(200); + + expect(header.warning).to.be( + '299 Kibana "Deprecated endpoint: /api/detection_engine/rules/_bulk_create API is deprecated since v8.2. Please use the /api/detection_engine/rules/_bulk_action API instead. See https://www.elastic.co/guide/en/security/master/rule-api-overview.html for more detail."' + ); + }); + }); + describe('creating rules in bulk', () => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/auditbeat/hosts'); @@ -50,7 +68,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should create a single rule with a rule_id', async () => { const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_create`) + .post(DETECTION_ENGINE_RULES_BULK_CREATE) .set('kbn-xsrf', 'true') .send([getSimpleRule()]) .expect(200); @@ -81,7 +99,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should create a single rule with a rule_id and validate it ran successfully', async () => { const simpleRule = getRuleForSignalTesting(['auditbeat-*']); const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_create`) + .post(DETECTION_ENGINE_RULES_BULK_CREATE) .set('kbn-xsrf', 'true') .send([simpleRule]) .expect(200); @@ -91,7 +109,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should create a single rule without a rule_id', async () => { const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_create`) + .post(DETECTION_ENGINE_RULES_BULK_CREATE) .set('kbn-xsrf', 'true') .send([getSimpleRuleWithoutRuleId()]) .expect(200); @@ -102,7 +120,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should return a 200 ok but have a 409 conflict if we attempt to create the same rule_id twice', async () => { const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_create`) + .post(DETECTION_ENGINE_RULES_BULK_CREATE) .set('kbn-xsrf', 'true') .send([getSimpleRule(), getSimpleRule()]) .expect(200); @@ -120,13 +138,13 @@ export default ({ getService }: FtrProviderContext): void => { it('should return a 200 ok but have a 409 conflict if we attempt to create the same rule_id that already exists', async () => { await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_create`) + .post(DETECTION_ENGINE_RULES_BULK_CREATE) .set('kbn-xsrf', 'true') .send([getSimpleRule()]) .expect(200); const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_create`) + .post(DETECTION_ENGINE_RULES_BULK_CREATE) .set('kbn-xsrf', 'foo') .send([getSimpleRule()]) .expect(200); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_rules_bulk.ts index 69be1f2eb0aff..a2c20f8496049 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/delete_rules_bulk.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; import { BASE_ALERTING_API_PATH } from '../../../../plugins/alerting/common'; -import { DETECTION_ENGINE_RULES_URL } from '../../../../plugins/security_solution/common/constants'; +import { DETECTION_ENGINE_RULES_BULK_DELETE } from '../../../../plugins/security_solution/common/constants'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createLegacyRuleAction, @@ -31,6 +31,22 @@ export default ({ getService }: FtrProviderContext): void => { const log = getService('log'); describe('delete_rules_bulk', () => { + describe('deprecations', () => { + it('should return a warning header', async () => { + await createRule(supertest, log, getSimpleRule()); + + const { header } = await supertest + .delete(DETECTION_ENGINE_RULES_BULK_DELETE) + .set('kbn-xsrf', 'true') + .send([{ rule_id: 'rule-1' }]) + .expect(200); + + expect(header.warning).to.be( + '299 Kibana "Deprecated endpoint: /api/detection_engine/rules/_bulk_delete API is deprecated since v8.2. Please use the /api/detection_engine/rules/_bulk_action API instead. See https://www.elastic.co/guide/en/security/master/rule-api-overview.html for more detail."' + ); + }); + }); + describe('deleting rules bulk using DELETE', () => { beforeEach(async () => { await createSignalsIndex(supertest, log); @@ -46,7 +62,7 @@ export default ({ getService }: FtrProviderContext): void => { // delete the rule in bulk const { body } = await supertest - .delete(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .delete(DETECTION_ENGINE_RULES_BULK_DELETE) .set('kbn-xsrf', 'true') .send([{ rule_id: 'rule-1' }]) .expect(200); @@ -60,7 +76,7 @@ export default ({ getService }: FtrProviderContext): void => { // delete that rule by its rule_id const { body } = await supertest - .delete(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .delete(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ rule_id: bodyWithCreatedRule.rule_id }]) .set('kbn-xsrf', 'true') .expect(200); @@ -74,7 +90,7 @@ export default ({ getService }: FtrProviderContext): void => { // delete that rule by its id const { body } = await supertest - .delete(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .delete(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ id: bodyWithCreatedRule.id }]) .set('kbn-xsrf', 'true') .expect(200); @@ -85,7 +101,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should return an error if the ruled_id does not exist when trying to delete a rule_id', async () => { const { body } = await supertest - .delete(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .delete(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ rule_id: 'fake_id' }]) .set('kbn-xsrf', 'true') .expect(200); @@ -103,7 +119,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should return an error if the id does not exist when trying to delete an id', async () => { const { body } = await supertest - .delete(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .delete(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ id: 'c4e80a0d-e20f-4efc-84c1-08112da5a612' }]) .set('kbn-xsrf', 'true') .expect(200); @@ -123,7 +139,7 @@ export default ({ getService }: FtrProviderContext): void => { const bodyWithCreatedRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); const { body } = await supertest - .delete(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .delete(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ id: bodyWithCreatedRule.id }, { id: 'c4e80a0d-e20f-4efc-84c1-08112da5a612' }]) .set('kbn-xsrf', 'true') .expect(200); @@ -158,7 +174,7 @@ export default ({ getService }: FtrProviderContext): void => { // delete the rule in bulk const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .post(DETECTION_ENGINE_RULES_BULK_DELETE) .set('kbn-xsrf', 'true') .send([{ rule_id: 'rule-1' }]) .expect(200); @@ -172,7 +188,7 @@ export default ({ getService }: FtrProviderContext): void => { // delete that rule by its rule_id const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .post(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ rule_id: bodyWithCreatedRule.rule_id }]) .set('kbn-xsrf', 'true') .expect(200); @@ -186,7 +202,7 @@ export default ({ getService }: FtrProviderContext): void => { // delete that rule by its id const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .post(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ id: bodyWithCreatedRule.id }]) .set('kbn-xsrf', 'true') .expect(200); @@ -197,7 +213,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should return an error if the ruled_id does not exist when trying to delete a rule_id', async () => { const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .post(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ rule_id: 'fake_id' }]) .set('kbn-xsrf', 'true') .expect(200); @@ -215,7 +231,7 @@ export default ({ getService }: FtrProviderContext): void => { it('should return an error if the id does not exist when trying to delete an id', async () => { const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .post(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ id: 'c4e80a0d-e20f-4efc-84c1-08112da5a612' }]) .set('kbn-xsrf', 'true') .expect(200); @@ -235,7 +251,7 @@ export default ({ getService }: FtrProviderContext): void => { const bodyWithCreatedRule = await createRule(supertest, log, getSimpleRuleWithoutRuleId()); const { body } = await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .post(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ id: bodyWithCreatedRule.id }, { id: 'c4e80a0d-e20f-4efc-84c1-08112da5a612' }]) .set('kbn-xsrf', 'true') .expect(200); @@ -272,7 +288,7 @@ export default ({ getService }: FtrProviderContext): void => { // delete the rule with the legacy action const { body } = await supertest - .delete(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .delete(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ id: createRuleBody.id }]) .set('kbn-xsrf', 'true') .expect(200); @@ -320,7 +336,7 @@ export default ({ getService }: FtrProviderContext): void => { // delete 2 rules where both have legacy actions const { body } = await supertest - .delete(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .delete(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ id: createRuleBody1.id }, { id: createRuleBody2.id }]) .set('kbn-xsrf', 'true') .expect(200); @@ -372,7 +388,7 @@ export default ({ getService }: FtrProviderContext): void => { // bulk delete the rule await supertest - .delete(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) + .delete(DETECTION_ENGINE_RULES_BULK_DELETE) .send([{ id: createRuleBody.id }]) .set('kbn-xsrf', 'true') .expect(200); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/patch_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/patch_rules_bulk.ts index 51cf1a334a2c7..e860c097c9964 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/patch_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/patch_rules_bulk.ts @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; -import { DETECTION_ENGINE_RULES_URL } from '../../../../plugins/security_solution/common/constants'; +import { DETECTION_ENGINE_RULES_BULK_UPDATE } from '../../../../plugins/security_solution/common/constants'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createSignalsIndex, @@ -28,6 +28,26 @@ export default ({ getService }: FtrProviderContext) => { const log = getService('log'); describe('patch_rules_bulk', () => { + describe('deprecations', () => { + afterEach(async () => { + await deleteAllAlerts(supertest, log); + }); + + it('should return a warning header', async () => { + await createRule(supertest, log, getSimpleRule('rule-1')); + + const { header } = await supertest + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) + .set('kbn-xsrf', 'true') + .send([{ rule_id: 'rule-1', name: 'some other name' }]) + .expect(200); + + expect(header.warning).to.be( + '299 Kibana "Deprecated endpoint: /api/detection_engine/rules/_bulk_update API is deprecated since v8.2. Please use the /api/detection_engine/rules/_bulk_action API instead. See https://www.elastic.co/guide/en/security/master/rule-api-overview.html for more detail."' + ); + }); + }); + describe('patch rules bulk', () => { beforeEach(async () => { await createSignalsIndex(supertest, log); @@ -43,7 +63,7 @@ export default ({ getService }: FtrProviderContext) => { // patch a simple rule's name const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([{ rule_id: 'rule-1', name: 'some other name' }]) .expect(200); @@ -61,7 +81,7 @@ export default ({ getService }: FtrProviderContext) => { // patch both rule names const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([ { rule_id: 'rule-1', name: 'some other name' }, @@ -88,7 +108,7 @@ export default ({ getService }: FtrProviderContext) => { // patch a simple rule's name const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([{ id: createRuleBody.id, name: 'some other name' }]) .expect(200); @@ -106,7 +126,7 @@ export default ({ getService }: FtrProviderContext) => { // patch both rule names const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([ { id: createRule1.id, name: 'some other name' }, @@ -149,7 +169,7 @@ export default ({ getService }: FtrProviderContext) => { ]); // patch a simple rule's name const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([ { id: rule1.id, enabled: false }, @@ -182,7 +202,7 @@ export default ({ getService }: FtrProviderContext) => { // patch a simple rule's name const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([{ id: createdBody.id, name: 'some other name' }]) .expect(200); @@ -199,7 +219,7 @@ export default ({ getService }: FtrProviderContext) => { // patch a simple rule's enabled to false const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([{ rule_id: 'rule-1', enabled: false }]) .expect(200); @@ -216,7 +236,7 @@ export default ({ getService }: FtrProviderContext) => { // patch a simple rule's enabled to false and another property const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([{ rule_id: 'rule-1', severity: 'low', enabled: false }]) .expect(200); @@ -235,14 +255,14 @@ export default ({ getService }: FtrProviderContext) => { // patch a simple rule's timeline_title await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([{ rule_id: 'rule-1', timeline_title: 'some title', timeline_id: 'some id' }]) .expect(200); // patch a simple rule's name const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([{ rule_id: 'rule-1', name: 'some other name' }]) .expect(200); @@ -259,7 +279,7 @@ export default ({ getService }: FtrProviderContext) => { it('should return a 200 but give a 404 in the message if it is given a fake id', async () => { const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([{ id: '5096dec6-b6b9-4d8d-8f93-6c2602079d9d', name: 'some other name' }]) .expect(200); @@ -277,7 +297,7 @@ export default ({ getService }: FtrProviderContext) => { it('should return a 200 but give a 404 in the message if it is given a fake rule_id', async () => { const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([{ rule_id: 'fake_id', name: 'some other name' }]) .expect(200); @@ -295,7 +315,7 @@ export default ({ getService }: FtrProviderContext) => { // patch one rule name and give a fake id for the second const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([ { rule_id: 'rule-1', name: 'some other name' }, @@ -325,7 +345,7 @@ export default ({ getService }: FtrProviderContext) => { // patch one rule name and give a fake id for the second const { body } = await supertest - .patch(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .patch(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([ { id: createdBody.id, name: 'some other name' }, diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/update_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/update_rules_bulk.ts index b165258237b41..e754cb2c02080 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/update_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/update_rules_bulk.ts @@ -8,7 +8,10 @@ import expect from '@kbn/expect'; import { FullResponseSchema } from '../../../../plugins/security_solution/common/detection_engine/schemas/request'; -import { DETECTION_ENGINE_RULES_URL } from '../../../../plugins/security_solution/common/constants'; +import { + DETECTION_ENGINE_RULES_URL, + DETECTION_ENGINE_RULES_BULK_UPDATE, +} from '../../../../plugins/security_solution/common/constants'; import { FtrProviderContext } from '../../common/ftr_provider_context'; import { createSignalsIndex, @@ -30,6 +33,27 @@ export default ({ getService }: FtrProviderContext) => { const log = getService('log'); describe('update_rules_bulk', () => { + describe('deprecations', () => { + afterEach(async () => { + await deleteAllAlerts(supertest, log); + }); + + it('should return a warning header', async () => { + await createRule(supertest, log, getSimpleRule('rule-1')); + const updatedRule = getSimpleRuleUpdate('rule-1'); + + const { header } = await supertest + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) + .set('kbn-xsrf', 'true') + .send([updatedRule]) + .expect(200); + + expect(header.warning).to.be( + '299 Kibana "Deprecated endpoint: /api/detection_engine/rules/_bulk_update API is deprecated since v8.2. Please use the /api/detection_engine/rules/_bulk_action API instead. See https://www.elastic.co/guide/en/security/master/rule-api-overview.html for more detail."' + ); + }); + }); + describe('update rules bulk', () => { beforeEach(async () => { await createSignalsIndex(supertest, log); @@ -48,7 +72,7 @@ export default ({ getService }: FtrProviderContext) => { // update a simple rule's name const { body } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([updatedRule]) .expect(200); @@ -78,7 +102,7 @@ export default ({ getService }: FtrProviderContext) => { // update both rule names const { body } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([updatedRule1, updatedRule2]) .expect(200); @@ -137,7 +161,7 @@ export default ({ getService }: FtrProviderContext) => { // update both rule names const { body }: { body: FullResponseSchema[] } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([updatedRule1, updatedRule2]) .expect(200); @@ -199,7 +223,7 @@ export default ({ getService }: FtrProviderContext) => { // update both rule names const { body }: { body: FullResponseSchema[] } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([updatedRule1, updatedRule2]) .expect(200); @@ -225,7 +249,7 @@ export default ({ getService }: FtrProviderContext) => { delete updatedRule1.rule_id; const { body } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([updatedRule1]) .expect(200); @@ -253,7 +277,7 @@ export default ({ getService }: FtrProviderContext) => { delete updatedRule2.rule_id; const { body } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([updatedRule1, updatedRule2]) .expect(200); @@ -282,7 +306,7 @@ export default ({ getService }: FtrProviderContext) => { delete updatedRule1.rule_id; const { body } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([updatedRule1]) .expect(200); @@ -303,7 +327,7 @@ export default ({ getService }: FtrProviderContext) => { updatedRule1.enabled = false; const { body } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([updatedRule1]) .expect(200); @@ -326,7 +350,7 @@ export default ({ getService }: FtrProviderContext) => { ruleUpdate.timeline_id = 'some id'; await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([ruleUpdate]) .expect(200); @@ -336,7 +360,7 @@ export default ({ getService }: FtrProviderContext) => { ruleUpdate2.name = 'some other name'; const { body } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([ruleUpdate2]) .expect(200); @@ -355,7 +379,7 @@ export default ({ getService }: FtrProviderContext) => { delete ruleUpdate.rule_id; const { body } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([ruleUpdate]) .expect(200); @@ -377,7 +401,7 @@ export default ({ getService }: FtrProviderContext) => { delete ruleUpdate.id; const { body } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([ruleUpdate]) .expect(200); @@ -403,7 +427,7 @@ export default ({ getService }: FtrProviderContext) => { // update one rule name and give a fake id for the second const { body } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([ruleUpdate, ruleUpdate2]) .expect(200); @@ -440,7 +464,7 @@ export default ({ getService }: FtrProviderContext) => { rule2.name = 'some other name'; const { body } = await supertest - .put(`${DETECTION_ENGINE_RULES_URL}/_bulk_update`) + .put(DETECTION_ENGINE_RULES_BULK_UPDATE) .set('kbn-xsrf', 'true') .send([rule1, rule2]) .expect(200); diff --git a/x-pack/test/detection_engine_api_integration/utils.ts b/x-pack/test/detection_engine_api_integration/utils.ts index de66002343212..2087e0d6ab523 100644 --- a/x-pack/test/detection_engine_api_integration/utils.ts +++ b/x-pack/test/detection_engine_api_integration/utils.ts @@ -52,6 +52,7 @@ import { DETECTION_ENGINE_INDEX_URL, DETECTION_ENGINE_PREPACKAGED_URL, DETECTION_ENGINE_QUERY_SIGNALS_URL, + DETECTION_ENGINE_RULES_BULK_ACTION, DETECTION_ENGINE_RULES_URL, DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL, DETECTION_ENGINE_SIGNALS_MIGRATION_URL, @@ -513,18 +514,9 @@ export const deleteAllAlerts = async ( ): Promise => { await countDownTest( async () => { - const { body } = await supertest - .get(`${DETECTION_ENGINE_RULES_URL}/_find?per_page=9999`) - .set('kbn-xsrf', 'true') - .send(); - - const ids = body.data.map((rule: FullResponseSchema) => ({ - id: rule.id, - })); - await supertest - .post(`${DETECTION_ENGINE_RULES_URL}/_bulk_delete`) - .send(ids) + .post(DETECTION_ENGINE_RULES_BULK_ACTION) + .send({ action: 'delete', query: '' }) .set('kbn-xsrf', 'true'); const { body: finalCheck } = await supertest