diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/alert_tags/index.ts b/x-pack/plugins/security_solution/common/api/detection_engine/alert_tags/index.ts index 7adc565a09c8e..0e4f6a7ae6609 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/alert_tags/index.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/alert_tags/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export * from './set_alert_tags/set_alert_tags_route'; +export * from './set_alert_tags/set_alert_tags.gen'; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags.gen.ts new file mode 100644 index 0000000000000..3640d78cd33b2 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags.gen.ts @@ -0,0 +1,38 @@ +/* + * 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: Manage alert tags API endpoint + * version: 2023-10-31 + */ + +import { z } from 'zod'; + +import { AlertIds, AlertTags } from '../../../model/alert.gen'; + +export type ManageAlertTags = z.infer; +export const ManageAlertTags = z.object({ + tags_to_add: AlertTags, + tags_to_remove: AlertTags, +}); + +export type ManageAlertTagsRequestBody = z.infer; +export const ManageAlertTagsRequestBody = z.object({ + ids: AlertIds, + tags: ManageAlertTags, +}); +export type ManageAlertTagsRequestBodyInput = z.input; + +/** + * Elasticsearch update by query response + */ +export type ManageAlertTagsResponse = z.infer; +export const ManageAlertTagsResponse = z.object({}).catchall(z.unknown()); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags.schema.yaml new file mode 100644 index 0000000000000..ca2c93b88b25e --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags.schema.yaml @@ -0,0 +1,70 @@ +openapi: 3.0.0 +info: + title: Manage alert tags API endpoint + version: '2023-10-31' +paths: + /api/detection_engine/signals/tags: + post: + x-labels: [serverless, ess] + operationId: ManageAlertTags + x-codegen-enabled: true + summary: Manage alert tags for a one or more alerts + tags: + - Alerts API + requestBody: + description: An object containing tags to add or remove and alert ids the changes will be applied + required: true + content: + application/json: + schema: + type: object + properties: + ids: + $ref: '../../../model/alert.schema.yaml#/components/schemas/AlertIds' + tags: + $ref: '#/components/schemas/ManageAlertTags' + required: + - ids + - tags + responses: + 200: + description: Successful response + content: + application/json: + schema: + type: object + additionalProperties: true + description: Elasticsearch update by query response + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../model/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../model/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../model/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../model/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + +components: + schemas: + ManageAlertTags: + type: object + properties: + tags_to_add: + $ref: '../../../model/alert.schema.yaml#/components/schemas/AlertTags' + tags_to_remove: + $ref: '../../../model/alert.schema.yaml#/components/schemas/AlertTags' + required: + - tags_to_add + - tags_to_remove diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags_route.mock.ts b/x-pack/plugins/security_solution/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags_route.mock.ts index f1e05ad7f125a..a14b36fb7b2c5 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags_route.mock.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags_route.mock.ts @@ -5,13 +5,13 @@ * 2.0. */ -import type { SetAlertTagsRequestBody } from './set_alert_tags_route'; +import type { ManageAlertTagsRequestBody } from './set_alert_tags.gen'; export const getSetAlertTagsRequestMock = ( tagsToAdd: string[] = [], tagsToRemove: string[] = [], ids: string[] = [] -): SetAlertTagsRequestBody => ({ +): ManageAlertTagsRequestBody => ({ tags: { tags_to_add: tagsToAdd, tags_to_remove: tagsToRemove }, ids, }); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags_route.ts b/x-pack/plugins/security_solution/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags_route.ts deleted file mode 100644 index 8a6f93c20b770..0000000000000 --- a/x-pack/plugins/security_solution/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags_route.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import * as t from 'io-ts'; - -import { alert_tag_ids, alert_tags } from '../../model'; - -export const setAlertTagsRequestBody = t.exact( - t.type({ - tags: alert_tags, - ids: alert_tag_ids, - }) -); - -export type SetAlertTagsRequestBody = t.TypeOf; -export type SetAlertTagsRequestBodyDecoded = SetAlertTagsRequestBody; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals/index.ts b/x-pack/plugins/security_solution/common/api/detection_engine/signals/index.ts index 636728ee2525d..97f441030bb48 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/signals/index.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/signals/index.ts @@ -5,6 +5,5 @@ * 2.0. */ -export * from './query_signals/query_signals_route'; -export * from './set_signal_status/set_signal_status_route'; -export * from './set_signal_status/set_signal_status_type_dependents'; +export * from './query_signals/query_signals_route.gen'; +export * from './set_signal_status/set_signals_status_route.gen'; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals/query_signals/query_signals_route.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/signals/query_signals/query_signals_route.gen.ts new file mode 100644 index 0000000000000..67c5a695d1949 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/signals/query_signals/query_signals_route.gen.ts @@ -0,0 +1,45 @@ +/* + * 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: Alerts search API endpoint + * version: 2023-10-31 + */ + +import { z } from 'zod'; + +export type AlertsSortCombinations = z.infer; +export const AlertsSortCombinations = z.union([z.string(), z.object({}).catchall(z.unknown())]); + +export type AlertsSort = z.infer; +export const AlertsSort = z.union([AlertsSortCombinations, z.array(AlertsSortCombinations)]); + +/** + * Elasticsearch query and aggregation request + */ +export type SearchAlertsRequestBody = z.infer; +export const SearchAlertsRequestBody = z.object({ + query: z.object({}).catchall(z.unknown()).optional(), + aggs: z.object({}).catchall(z.unknown()).optional(), + size: z.number().int().min(0).optional(), + track_total_hits: z.boolean().optional(), + _source: z.union([z.boolean(), z.string(), z.array(z.string())]).optional(), + fields: z.array(z.string()).optional(), + runtime_mappings: z.object({}).catchall(z.unknown()).optional(), + sort: AlertsSort.optional(), +}); +export type SearchAlertsRequestBodyInput = z.input; + +/** + * Elasticsearch search response + */ +export type SearchAlertsResponse = z.infer; +export const SearchAlertsResponse = z.object({}).catchall(z.unknown()); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals/query_signals/query_signals_route.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/signals/query_signals/query_signals_route.schema.yaml new file mode 100644 index 0000000000000..cd70e4b0c4071 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/signals/query_signals/query_signals_route.schema.yaml @@ -0,0 +1,93 @@ +openapi: 3.0.0 +info: + title: Alerts search API endpoint + version: '2023-10-31' +paths: + /api/detection_engine/signals/search: + post: + x-labels: [serverless, ess] + operationId: SearchAlerts + x-codegen-enabled: true + summary: Find and/or aggregate detection alerts that match the given query + tags: + - Alerts API + requestBody: + description: Search and/or aggregation query + required: true + content: + application/json: + schema: + type: object + properties: + query: + type: object + additionalProperties: true + aggs: + type: object + additionalProperties: true + size: + type: integer + minimum: 0 + track_total_hits: + type: boolean + _source: + oneOf: + - type: boolean + - type: string + - type: array + items: + type: string + fields: + type: array + items: + type: string + runtime_mappings: + type: object + additionalProperties: true + sort: + $ref: '#/components/schemas/AlertsSort' + description: Elasticsearch query and aggregation request + responses: + 200: + description: Successful response + content: + application/json: + schema: + type: object + additionalProperties: true + description: Elasticsearch search response + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../model/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../model/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../model/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../model/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + +components: + schemas: + AlertsSortCombinations: + anyOf: + - type: string + - type: object + additionalProperties: true + + AlertsSort: + oneOf: + - $ref: '#/components/schemas/AlertsSortCombinations' + - type: array + items: + $ref: '#/components/schemas/AlertsSortCombinations' diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals/query_signals/query_signals_route.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/signals/query_signals/query_signals_route.test.ts deleted file mode 100644 index 6afdc9a07e8ab..0000000000000 --- a/x-pack/plugins/security_solution/common/api/detection_engine/signals/query_signals/query_signals_route.test.ts +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { QuerySignalsSchema } from './query_signals_route'; -import { querySignalsSchema } from './query_signals_route'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; - -describe('query, aggs, size, _source and track_total_hits on signals index', () => { - test('query, aggs, size, _source and track_total_hits simultaneously', () => { - const payload: QuerySignalsSchema = { - query: {}, - aggs: {}, - size: 1, - track_total_hits: true, - _source: ['field'], - }; - - const decoded = querySignalsSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('query, only', () => { - const payload: QuerySignalsSchema = { - query: {}, - }; - - const decoded = querySignalsSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('aggs only', () => { - const payload: QuerySignalsSchema = { - aggs: {}, - }; - - const decoded = querySignalsSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('size only', () => { - const payload: QuerySignalsSchema = { - size: 1, - }; - - const decoded = querySignalsSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('track_total_hits only', () => { - const payload: QuerySignalsSchema = { - track_total_hits: true, - }; - - const decoded = querySignalsSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('_source only (as string)', () => { - const payload: QuerySignalsSchema = { - _source: 'field', - }; - - const decoded = querySignalsSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('_source only (as string[])', () => { - const payload: QuerySignalsSchema = { - _source: ['field'], - }; - - const decoded = querySignalsSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('_source only (as boolean)', () => { - const payload: QuerySignalsSchema = { - _source: false, - }; - - const decoded = querySignalsSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('fields only', () => { - const payload: QuerySignalsSchema = { - fields: ['test*'], - }; - - const decoded = querySignalsSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('sort only', () => { - const payload: QuerySignalsSchema = { - sort: { - '@payload': 'desc', - }, - }; - - const decoded = querySignalsSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); -}); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals/query_signals/query_signals_route.ts b/x-pack/plugins/security_solution/common/api/detection_engine/signals/query_signals/query_signals_route.ts deleted file mode 100644 index 7c0283f3f2a80..0000000000000 --- a/x-pack/plugins/security_solution/common/api/detection_engine/signals/query_signals/query_signals_route.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import * as t from 'io-ts'; -import { PositiveInteger } from '@kbn/securitysolution-io-ts-types'; - -export const querySignalsSchema = t.exact( - t.partial({ - query: t.object, - aggs: t.object, - size: PositiveInteger, - track_total_hits: t.boolean, - _source: t.union([t.boolean, t.string, t.array(t.string)]), - fields: t.array(t.string), - runtime_mappings: t.unknown, - sort: t.object, - }) -); - -export type QuerySignalsSchema = t.TypeOf; -export type QuerySignalsSchemaDecoded = QuerySignalsSchema; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signal_status_route.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signal_status_route.test.ts deleted file mode 100644 index 3f9e432fbc4f9..0000000000000 --- a/x-pack/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signal_status_route.test.ts +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { SetSignalsStatusSchema } from './set_signal_status_route'; -import { setSignalsStatusSchema } from './set_signal_status_route'; -import { exactCheck, foldLeftRight, getPaths } from '@kbn/securitysolution-io-ts-utils'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { left } from 'fp-ts/lib/Either'; - -describe('set signal status schema', () => { - test('signal_ids and status is valid', () => { - const payload: SetSignalsStatusSchema = { - signal_ids: ['somefakeid'], - status: 'open', - }; - - const decoded = setSignalsStatusSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('query and status is valid', () => { - const payload: SetSignalsStatusSchema = { - query: {}, - status: 'open', - }; - - const decoded = setSignalsStatusSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(payload); - }); - - test('signal_ids and missing status is invalid', () => { - const payload: Omit = { - signal_ids: ['somefakeid'], - }; - - const decoded = setSignalsStatusSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "status"', - ]); - expect(message.schema).toEqual({}); - }); - - test('query and missing status is invalid', () => { - const payload: Omit = { - query: {}, - }; - - const decoded = setSignalsStatusSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "undefined" supplied to "status"', - ]); - expect(message.schema).toEqual({}); - }); - - test('signal_ids is present but status has wrong value', () => { - const payload: Omit & { status: 'fakeVal' } = { - query: {}, - status: 'fakeVal', - }; - - const decoded = setSignalsStatusSchema.decode(payload); - const checked = exactCheck(payload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([ - 'Invalid value "fakeVal" supplied to "status"', - ]); - expect(message.schema).toEqual({}); - }); -}); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signal_status_route.ts b/x-pack/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signal_status_route.ts deleted file mode 100644 index 78d62031d5eb3..0000000000000 --- a/x-pack/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signal_status_route.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import * as t from 'io-ts'; - -import { conflicts, signal_ids, signal_status_query, status } from '../../model'; - -export const setSignalsStatusSchema = t.intersection([ - t.type({ - status, - }), - t.partial({ - conflicts, - signal_ids, - query: signal_status_query, - }), -]); - -export type SetSignalsStatusSchema = t.TypeOf; -export type SetSignalsStatusSchemaDecoded = SetSignalsStatusSchema; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signal_status_type_dependents.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signal_status_type_dependents.test.ts deleted file mode 100644 index 97cf74435c294..0000000000000 --- a/x-pack/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signal_status_type_dependents.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { setSignalStatusValidateTypeDependents } from './set_signal_status_type_dependents'; -import type { SetSignalsStatusSchema } from './set_signal_status_route'; - -describe('update_rules_type_dependents', () => { - test('You can have just a "signals_id"', () => { - const schema: SetSignalsStatusSchema = { - status: 'open', - signal_ids: ['some-id'], - }; - const errors = setSignalStatusValidateTypeDependents(schema); - expect(errors).toEqual([]); - }); - - test('You can have just a "query"', () => { - const schema: SetSignalsStatusSchema = { - status: 'open', - query: {}, - }; - const errors = setSignalStatusValidateTypeDependents(schema); - expect(errors).toEqual([]); - }); - - test('You cannot have both a "signals_id" and a "query"', () => { - const schema: SetSignalsStatusSchema = { - status: 'open', - query: {}, - signal_ids: ['some-id'], - }; - const errors = setSignalStatusValidateTypeDependents(schema); - expect(errors).toEqual(['both "signal_ids" and "query" cannot exist, choose one or the other']); - }); - - test('You must set either an "signals_id" and a "query"', () => { - const schema: SetSignalsStatusSchema = { - status: 'open', - }; - const errors = setSignalStatusValidateTypeDependents(schema); - expect(errors).toEqual(['either "signal_ids" or "query" must be set']); - }); -}); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signal_status_type_dependents.ts b/x-pack/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signal_status_type_dependents.ts deleted file mode 100644 index a484fde33d107..0000000000000 --- a/x-pack/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signal_status_type_dependents.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { SetSignalsStatusSchema } from './set_signal_status_route'; - -export const validateId = (signalStatus: SetSignalsStatusSchema): string[] => { - if (signalStatus.signal_ids != null && signalStatus.query != null) { - return ['both "signal_ids" and "query" cannot exist, choose one or the other']; - } else if (signalStatus.signal_ids == null && signalStatus.query == null) { - return ['either "signal_ids" or "query" must be set']; - } else { - return []; - } -}; - -export const setSignalStatusValidateTypeDependents = (schema: SetSignalsStatusSchema): string[] => { - return [...validateId(schema)]; -}; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signals_status_route.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signals_status_route.gen.ts new file mode 100644 index 0000000000000..a64a35d16d83e --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signals_status_route.gen.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + * + * info: + * title: Set alerts status API endpoint + * version: 2023-10-31 + */ + +import { z } from 'zod'; + +import { NonEmptyString } from '../../../model/primitives.gen'; +import { AlertStatus } from '../../../model/alert.gen'; + +export type SetAlertsStatusByIds = z.infer; +export const SetAlertsStatusByIds = z.object({ + signal_ids: z.array(NonEmptyString).min(1), + status: AlertStatus, +}); + +export type SetAlertsStatusByQuery = z.infer; +export const SetAlertsStatusByQuery = z.object({ + query: z.object({}).catchall(z.unknown()), + status: AlertStatus, + conflicts: z.enum(['abort', 'proceed']).optional().default('abort'), +}); + +export type SetAlertsStatusRequestBody = z.infer; +export const SetAlertsStatusRequestBody = z.union([SetAlertsStatusByIds, SetAlertsStatusByQuery]); +export type SetAlertsStatusRequestBodyInput = z.input; + +/** + * Elasticsearch update by query response + */ +export type SetAlertsStatusResponse = z.infer; +export const SetAlertsStatusResponse = z.object({}).catchall(z.unknown()); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signals_status_route.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signals_status_route.schema.yaml new file mode 100644 index 0000000000000..29ee065c77e6b --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/signals/set_signal_status/set_signals_status_route.schema.yaml @@ -0,0 +1,81 @@ +openapi: 3.0.0 +info: + title: Set alerts status API endpoint + version: '2023-10-31' +paths: + /api/detection_engine/signals/status: + post: + x-labels: [serverless, ess] + operationId: SetAlertsStatus + x-codegen-enabled: true + summary: Sets the status of one or more alerts + tags: + - Alerts API + requestBody: + description: An object containing desired status and explicit alert ids or a query to select alerts + required: true + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/SetAlertsStatusByIds' + - $ref: '#/components/schemas/SetAlertsStatusByQuery' + responses: + 200: + description: Successful response + content: + application/json: + schema: + type: object + additionalProperties: true + description: Elasticsearch update by query response + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../model/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../model/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../model/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../model/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + +components: + schemas: + SetAlertsStatusByIds: + type: object + properties: + signal_ids: + type: array + items: + $ref: '../../../model/primitives.schema.yaml#/components/schemas/NonEmptyString' + minItems: 1 + status: + $ref: '../../../model/alert.schema.yaml#/components/schemas/AlertStatus' + required: [signal_ids, status] + + SetAlertsStatusByQuery: + type: object + properties: + query: + type: object + additionalProperties: true + status: + $ref: '../../../model/alert.schema.yaml#/components/schemas/AlertStatus' + conflicts: + type: string + enum: + - abort + - proceed + default: abort + required: [query, status] diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration.gen.ts new file mode 100644 index 0000000000000..f82dedc27a289 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration.gen.ts @@ -0,0 +1,64 @@ +/* + * 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: Initiates alerts migration API endpoint + * version: 2023-10-31 + */ + +import { z } from 'zod'; + +import { NonEmptyString } from '../../../model/primitives.gen'; + +export type AlertsReindexOptions = z.infer; +export const AlertsReindexOptions = z.object({ + requests_per_second: z.number().int().min(1).optional(), + size: z.number().int().min(1).optional(), + slices: z.number().int().min(1).optional(), +}); + +export type AlertsIndexMigrationSuccess = z.infer; +export const AlertsIndexMigrationSuccess = z.object({ + index: z.string(), + migration_id: z.string(), + migration_index: z.string(), +}); + +export type AlertsIndexMigrationError = z.infer; +export const AlertsIndexMigrationError = z.object({ + index: z.string(), + error: z.object({ + message: z.string(), + status_code: z.string(), + }), +}); + +export type SkippedAlertsIndexMigration = z.infer; +export const SkippedAlertsIndexMigration = z.object({ + index: z.string(), +}); + +export type CreateAlertsMigrationRequestBody = z.infer; +export const CreateAlertsMigrationRequestBody = z + .object({ + index: z.array(NonEmptyString).min(1), + }) + .merge(AlertsReindexOptions); +export type CreateAlertsMigrationRequestBodyInput = z.input< + typeof CreateAlertsMigrationRequestBody +>; + +export type CreateAlertsMigrationResponse = z.infer; +export const CreateAlertsMigrationResponse = z.object({ + indices: z.array( + z.union([AlertsIndexMigrationSuccess, AlertsIndexMigrationError, SkippedAlertsIndexMigration]) + ), +}); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration.schema.yaml new file mode 100644 index 0000000000000..26204ea0d6195 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration.schema.yaml @@ -0,0 +1,120 @@ +openapi: 3.0.0 +info: + title: Initiates alerts migration API endpoint + version: '2023-10-31' +paths: + /api/detection_engine/signals/migration: + post: + x-labels: [ess] + operationId: CreateAlertsMigration + x-codegen-enabled: true + summary: Initiates an alerts migration + tags: + - Alerts migration API + requestBody: + description: Alerts migration parameters + required: true + content: + application/json: + schema: + allOf: + - type: object + properties: + index: + type: array + items: + $ref: '../../../model/primitives.schema.yaml#/components/schemas/NonEmptyString' + minItems: 1 + required: [index] + - $ref: '#/components/schemas/AlertsReindexOptions' + + responses: + 200: + description: Successful response + content: + application/json: + schema: + type: object + properties: + indices: + type: array + items: + oneOf: + - $ref: '#/components/schemas/AlertsIndexMigrationSuccess' + - $ref: '#/components/schemas/AlertsIndexMigrationError' + - $ref: '#/components/schemas/SkippedAlertsIndexMigration' + required: [indices] + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../model/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../model/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../model/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../model/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + +components: + schemas: + AlertsReindexOptions: + type: object + properties: + requests_per_second: + type: integer + minimum: 1 + size: + type: integer + minimum: 1 + slices: + type: integer + minimum: 1 + + AlertsIndexMigrationSuccess: + type: object + properties: + index: + type: string + migration_id: + type: string + migration_index: + type: string + required: + - index + - migration_id + - migration_index + + AlertsIndexMigrationError: + type: object + properties: + index: + type: string + error: + type: object + properties: + message: + type: string + status_code: + type: string + required: [message, status_code] + required: + - index + - error + + SkippedAlertsIndexMigration: + type: object + properties: + index: + type: string + required: + - index diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration_route.mock.ts b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration_route.mock.ts index ccb5543ef72d2..09a94fc86224c 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration_route.mock.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration_route.mock.ts @@ -5,10 +5,10 @@ * 2.0. */ -import type { CreateSignalsMigrationSchema } from './create_signals_migration_route'; +import type { CreateAlertsMigrationRequestBody } from './create_signals_migration.gen'; export const getCreateSignalsMigrationSchemaMock = ( index: string = 'signals-index' -): CreateSignalsMigrationSchema => ({ +): CreateAlertsMigrationRequestBody => ({ index: [index], }); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration_route.ts b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration_route.ts deleted file mode 100644 index 9089efeb87d05..0000000000000 --- a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration_route.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import * as t from 'io-ts'; - -import { PositiveInteger, PositiveIntegerGreaterThanZero } from '@kbn/securitysolution-io-ts-types'; - -export const signalsReindexOptions = t.partial({ - requests_per_second: t.number, - size: PositiveIntegerGreaterThanZero, - slices: PositiveInteger, -}); - -export type SignalsReindexOptions = t.TypeOf; - -export const createSignalsMigrationSchema = t.intersection([ - t.exact( - t.type({ - index: t.array(t.string), - }) - ), - t.exact(signalsReindexOptions), -]); - -export type CreateSignalsMigrationSchema = t.TypeOf; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/delete_signals_migration/delete_signals_migration.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/delete_signals_migration/delete_signals_migration.gen.ts new file mode 100644 index 0000000000000..c82407052c972 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/delete_signals_migration/delete_signals_migration.gen.ts @@ -0,0 +1,44 @@ +/* + * 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: Alerts migration cleanup API endpoint + * version: 2023-10-31 + */ + +import { z } from 'zod'; + +export type MigrationCleanupResult = z.infer; +export const MigrationCleanupResult = z.object({ + id: z.string(), + destinationIndex: z.string(), + status: z.enum(['success', 'failure', 'pending']), + sourceIndex: z.string(), + version: z.string(), + updated: z.string().datetime(), + error: z + .object({ + message: z.string(), + status_code: z.number().int(), + }) + .optional(), +}); + +export type AlertsMigrationCleanupRequestBody = z.infer; +export const AlertsMigrationCleanupRequestBody = z.object({ + migration_ids: z.array(z.string()).min(1), +}); +export type AlertsMigrationCleanupRequestBodyInput = z.input< + typeof AlertsMigrationCleanupRequestBody +>; + +export type AlertsMigrationCleanupResponse = z.infer; +export const AlertsMigrationCleanupResponse = z.array(MigrationCleanupResult); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/delete_signals_migration/delete_signals_migration.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/delete_signals_migration/delete_signals_migration.schema.yaml new file mode 100644 index 0000000000000..7b8136f3702cf --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/delete_signals_migration/delete_signals_migration.schema.yaml @@ -0,0 +1,100 @@ +openapi: 3.0.0 +info: + title: Alerts migration cleanup API endpoint + version: '2023-10-31' +paths: + /api/detection_engine/signals/migration: + delete: + x-labels: [ess] + operationId: AlertsMigrationCleanup + x-codegen-enabled: true + summary: Performs alerts migration(s) cleanup + description: | + Migrations favor data integrity over shard size. Consequently, unused or orphaned indices are artifacts of + the migration process. A successful migration will result in both the old and new indices being present. + As such, the old, orphaned index can (and likely should) be deleted. While you can delete these indices manually, + the endpoint accomplishes this task by applying a deletion policy to the relevant index, causing it to be deleted + after 30 days. It also deletes other artifacts specific to the migration implementation. + tags: + - Alerts migration API + requestBody: + description: Array of `migration_id`s to cleanup + required: true + content: + application/json: + schema: + type: object + properties: + migration_ids: + type: array + items: + type: string + minItems: 1 + required: [migration_ids] + responses: + 200: + description: Successful response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/MigrationCleanupResult' + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../model/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../model/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../model/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../model/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + +components: + schemas: + MigrationCleanupResult: + type: object + properties: + id: + type: string + destinationIndex: + type: string + status: + type: string + enum: + - success + - failure + - pending + sourceIndex: + type: string + version: + type: string + updated: + type: string + format: date-time + error: + type: object + properties: + message: + type: string + status_code: + type: integer + required: [message, status_code] + required: + - id + - destinationIndex + - status + - sourceIndex + - version + - updated diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/delete_signals_migration/delete_signals_migration_route.ts b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/delete_signals_migration/delete_signals_migration_route.ts deleted file mode 100644 index 837f34f3ec14f..0000000000000 --- a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/delete_signals_migration/delete_signals_migration_route.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import * as t from 'io-ts'; - -export const deleteSignalsMigrationSchema = t.exact( - t.type({ - migration_ids: t.array(t.string), - }) -); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration.gen.ts new file mode 100644 index 0000000000000..cd18cad8e1cfa --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration.gen.ts @@ -0,0 +1,45 @@ +/* + * 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: Finalize alerts migration API endpoint + * version: 2023-10-31 + */ + +import { z } from 'zod'; + +export type MigrationFinalizationResult = z.infer; +export const MigrationFinalizationResult = z.object({ + id: z.string(), + completed: z.boolean(), + destinationIndex: z.string(), + status: z.enum(['success', 'failure', 'pending']), + sourceIndex: z.string(), + version: z.string(), + updated: z.string().datetime(), + error: z + .object({ + message: z.string(), + status_code: z.number().int(), + }) + .optional(), +}); + +export type FinalizeAlertsMigrationRequestBody = z.infer; +export const FinalizeAlertsMigrationRequestBody = z.object({ + migration_ids: z.array(z.string()).min(1), +}); +export type FinalizeAlertsMigrationRequestBodyInput = z.input< + typeof FinalizeAlertsMigrationRequestBody +>; + +export type FinalizeAlertsMigrationResponse = z.infer; +export const FinalizeAlertsMigrationResponse = z.array(MigrationFinalizationResult); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration.schema.yaml new file mode 100644 index 0000000000000..3654973f9de7e --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration.schema.yaml @@ -0,0 +1,101 @@ +openapi: 3.0.0 +info: + title: Finalize alerts migration API endpoint + version: '2023-10-31' +paths: + /api/detection_engine/signals/finalize_migration: + post: + x-labels: [ess] + operationId: FinalizeAlertsMigration + x-codegen-enabled: true + summary: Finalizes alerts migration(s) + description: | + The finalization endpoint replaces the original index's alias with the successfully migrated index's alias. + The endpoint is idempotent; therefore, it can safely be used to poll a given migration and, upon completion, + finalize it. + tags: + - Alerts migration API + requestBody: + description: Array of `migration_id`s to finalize + required: true + content: + application/json: + schema: + type: object + properties: + migration_ids: + type: array + items: + type: string + minItems: 1 + required: [migration_ids] + responses: + 200: + description: Successful response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/MigrationFinalizationResult' + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../model/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../model/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../model/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../model/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + +components: + schemas: + MigrationFinalizationResult: + type: object + properties: + id: + type: string + completed: + type: boolean + destinationIndex: + type: string + status: + type: string + enum: + - success + - failure + - pending + sourceIndex: + type: string + version: + type: string + updated: + type: string + format: date-time + error: + type: object + properties: + message: + type: string + status_code: + type: integer + required: [message, status_code] + required: + - id + - completed + - destinationIndex + - status + - sourceIndex + - version + - updated diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration_route.mock.ts b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration_route.mock.ts index 25d66c5b68d09..b43a04819e67f 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration_route.mock.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration_route.mock.ts @@ -5,8 +5,8 @@ * 2.0. */ -import type { FinalizeSignalsMigrationSchema } from './finalize_signals_migration_route'; +import type { FinalizeAlertsMigrationRequestBody } from './finalize_signals_migration.gen'; -export const getFinalizeSignalsMigrationSchemaMock = (): FinalizeSignalsMigrationSchema => ({ +export const getFinalizeSignalsMigrationSchemaMock = (): FinalizeAlertsMigrationRequestBody => ({ migration_ids: ['migrationSOIdentifier'], }); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration_route.ts b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration_route.ts deleted file mode 100644 index a4a48a7bc0c21..0000000000000 --- a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration_route.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import * as t from 'io-ts'; - -export const finalizeSignalsMigrationSchema = t.exact( - t.type({ - migration_ids: t.array(t.string), - }) -); - -export type FinalizeSignalsMigrationSchema = t.TypeOf; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/get_signals_migration_status/get_signals_migration_status.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/get_signals_migration_status/get_signals_migration_status.gen.ts new file mode 100644 index 0000000000000..acf9c9edd389d --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/get_signals_migration_status/get_signals_migration_status.gen.ts @@ -0,0 +1,61 @@ +/* + * 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: Get alerts migration status API endpoint + * version: 2023-10-31 + */ + +import { z } from 'zod'; +import { isValidDateMath } from '@kbn/zod-helpers'; + +import { NonEmptyString } from '../../../model/primitives.gen'; + +export type AlertVersion = z.infer; +export const AlertVersion = z.object({ + version: z.number().int(), + count: z.number().int(), +}); + +export type MigrationStatus = z.infer; +export const MigrationStatus = z.object({ + id: NonEmptyString, + status: z.enum(['success', 'failure', 'pending']), + version: z.number().int(), + updated: z.string().datetime(), +}); + +export type IndexMigrationStatus = z.infer; +export const IndexMigrationStatus = z.object({ + index: NonEmptyString, + version: z.number().int(), + signal_versions: z.array(AlertVersion), + migrations: z.array(MigrationStatus), + is_outdated: z.boolean(), +}); + +export type GetAlertsMigrationStatusRequestQuery = z.infer< + typeof GetAlertsMigrationStatusRequestQuery +>; +export const GetAlertsMigrationStatusRequestQuery = z.object({ + /** + * Maximum age of qualifying detection alerts + */ + from: z.string().superRefine(isValidDateMath), +}); +export type GetAlertsMigrationStatusRequestQueryInput = z.input< + typeof GetAlertsMigrationStatusRequestQuery +>; + +export type GetAlertsMigrationStatusResponse = z.infer; +export const GetAlertsMigrationStatusResponse = z.object({ + indices: z.array(IndexMigrationStatus), +}); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/get_signals_migration_status/get_signals_migration_status.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/get_signals_migration_status/get_signals_migration_status.schema.yaml new file mode 100644 index 0000000000000..b480b4374498b --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/get_signals_migration_status/get_signals_migration_status.schema.yaml @@ -0,0 +1,114 @@ +openapi: 3.0.0 +info: + title: Get alerts migration status API endpoint + version: '2023-10-31' +paths: + /api/detection_engine/signals/migration_status: + post: + x-labels: [ess] + operationId: GetAlertsMigrationStatus + x-codegen-enabled: true + summary: Returns an alerts migration status + tags: + - Alerts migration API + parameters: + - name: from + in: query + description: Maximum age of qualifying detection alerts + required: true + schema: + type: string + description: | + Time from which data is analyzed. For example, now-4200s means the rule analyzes data from 70 minutes + before its start time. Defaults to now-6m (analyzes data from 6 minutes before the start time). + format: date-math + responses: + 200: + description: Successful response + content: + application/json: + schema: + type: object + properties: + indices: + type: array + items: + $ref: '#/components/schemas/IndexMigrationStatus' + required: [indices] + 400: + description: Invalid input data response + content: + application/json: + schema: + oneOf: + - $ref: '../../../model/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + - $ref: '../../../model/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + 401: + description: Unsuccessful authentication response + content: + application/json: + schema: + $ref: '../../../model/error_responses.schema.yaml#/components/schemas/PlatformErrorResponse' + 500: + description: Internal server error response + content: + application/json: + schema: + $ref: '../../../model/error_responses.schema.yaml#/components/schemas/SiemErrorResponse' + +components: + schemas: + AlertVersion: + type: object + properties: + version: + type: integer + count: + type: integer + required: [version, count] + + MigrationStatus: + type: object + properties: + id: + $ref: '../../../model/primitives.schema.yaml#/components/schemas/NonEmptyString' + status: + type: string + enum: + - success + - failure + - pending + version: + type: integer + updated: + type: string + format: date-time + required: + - id + - status + - version + - updated + + IndexMigrationStatus: + type: object + properties: + index: + $ref: '../../../model/primitives.schema.yaml#/components/schemas/NonEmptyString' + version: + type: integer + signal_versions: + type: array + items: + $ref: '#/components/schemas/AlertVersion' + migrations: + type: array + items: + $ref: '#/components/schemas/MigrationStatus' + is_outdated: + type: boolean + required: + - index + - version + - signal_versions + - migrations + - is_outdated diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/get_signals_migration_status/get_signals_migration_status_route.mock.ts b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/get_signals_migration_status/get_signals_migration_status_route.mock.ts index 8cc78ffa9700e..968d72d6f40d0 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/get_signals_migration_status/get_signals_migration_status_route.mock.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/get_signals_migration_status/get_signals_migration_status_route.mock.ts @@ -5,8 +5,8 @@ * 2.0. */ -import type { GetSignalsMigrationStatusSchema } from './get_signals_migration_status_route'; +import type { GetAlertsMigrationStatusRequestQuery } from './get_signals_migration_status.gen'; -export const getSignalsMigrationStatusSchemaMock = (): GetSignalsMigrationStatusSchema => ({ +export const getSignalsMigrationStatusSchemaMock = (): GetAlertsMigrationStatusRequestQuery => ({ from: 'now-30d', }); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/get_signals_migration_status/get_signals_migration_status_route.ts b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/get_signals_migration_status/get_signals_migration_status_route.ts deleted file mode 100644 index f2a9fc210df2b..0000000000000 --- a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/get_signals_migration_status/get_signals_migration_status_route.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import * as t from 'io-ts'; - -import { RuleIntervalFrom } from '@kbn/securitysolution-io-ts-alerting-types'; - -export const getSignalsMigrationStatusSchema = t.exact( - t.type({ - from: RuleIntervalFrom, - }) -); - -export type GetSignalsMigrationStatusSchema = t.TypeOf; diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/index.ts b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/index.ts index 78452dc62f46c..84b00fc9f297c 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/index.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/signals_migration/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -export * from './create_signals_migration/create_signals_migration_route'; -export * from './delete_signals_migration/delete_signals_migration_route'; -export * from './finalize_signals_migration/finalize_signals_migration_route'; -export * from './get_signals_migration_status/get_signals_migration_status_route'; +export * from './create_signals_migration/create_signals_migration.gen'; +export * from './delete_signals_migration/delete_signals_migration.gen'; +export * from './finalize_signals_migration/finalize_signals_migration.gen'; +export * from './get_signals_migration_status/get_signals_migration_status.gen'; diff --git a/x-pack/plugins/security_solution/common/api/model/alert.gen.ts b/x-pack/plugins/security_solution/common/api/model/alert.gen.ts index 19ebcf2dee734..d149d36ab64b8 100644 --- a/x-pack/plugins/security_solution/common/api/model/alert.gen.ts +++ b/x-pack/plugins/security_solution/common/api/model/alert.gen.ts @@ -23,3 +23,14 @@ import { NonEmptyString } from './primitives.gen'; */ export type AlertIds = z.infer; export const AlertIds = z.array(NonEmptyString).min(1); + +export type AlertTag = z.infer; +export const AlertTag = NonEmptyString; + +export type AlertTags = z.infer; +export const AlertTags = z.array(AlertTag); + +export type AlertStatus = z.infer; +export const AlertStatus = z.enum(['open', 'closed', 'acknowledged', 'in-progress']); +export type AlertStatusEnum = typeof AlertStatus.enum; +export const AlertStatusEnum = AlertStatus.enum; diff --git a/x-pack/plugins/security_solution/common/api/model/alert.schema.yaml b/x-pack/plugins/security_solution/common/api/model/alert.schema.yaml index f28508dc620f2..ecf7e02d6ebe3 100644 --- a/x-pack/plugins/security_solution/common/api/model/alert.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/model/alert.schema.yaml @@ -12,3 +12,19 @@ components: $ref: './primitives.schema.yaml#/components/schemas/NonEmptyString' minItems: 1 description: A list of alerts ids. + + AlertTag: + $ref: './primitives.schema.yaml#/components/schemas/NonEmptyString' + + AlertTags: + type: array + items: + $ref: '#/components/schemas/AlertTag' + + AlertStatus: + type: string + enum: + - open + - closed + - acknowledged + - in-progress diff --git a/x-pack/plugins/security_solution/common/api/model/error_responses.gen.ts b/x-pack/plugins/security_solution/common/api/model/error_responses.gen.ts new file mode 100644 index 0000000000000..17d2c59bd338f --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/model/error_responses.gen.ts @@ -0,0 +1,30 @@ +/* + * 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: Error Schema + * version: not applicable + */ + +import { z } from 'zod'; + +export type PlatformErrorResponse = z.infer; +export const PlatformErrorResponse = z.object({ + statusCode: z.number().int(), + error: z.string(), + message: z.string(), +}); + +export type SiemErrorResponse = z.infer; +export const SiemErrorResponse = z.object({ + status_code: z.number().int(), + message: z.string(), +}); diff --git a/x-pack/plugins/security_solution/common/api/model/error_responses.schema.yaml b/x-pack/plugins/security_solution/common/api/model/error_responses.schema.yaml new file mode 100644 index 0000000000000..8f0891f3f574f --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/model/error_responses.schema.yaml @@ -0,0 +1,32 @@ +openapi: 3.0.0 +info: + title: Error Schema + version: 'not applicable' +paths: {} +components: + x-codegen-enabled: true + schemas: + PlatformErrorResponse: + type: object + properties: + statusCode: + type: integer + error: + type: string + message: + type: string + required: + - statusCode + - error + - message + + SiemErrorResponse: + type: object + properties: + status_code: + type: integer + message: + type: string + required: + - status_code + - message diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/migrations/create_migration.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/migrations/create_migration.ts index da49cda43507a..8bc38b1ecdf94 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/migrations/create_migration.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/migrations/create_migration.ts @@ -6,7 +6,7 @@ */ import type { ElasticsearchClient } from '@kbn/core/server'; -import type { SignalsReindexOptions } from '../../../../common/api/detection_engine/signals_migration'; +import type { AlertsReindexOptions } from '../../../../common/api/detection_engine/signals_migration'; import { createMigrationIndex } from './create_migration_index'; export interface CreatedMigration { @@ -24,7 +24,7 @@ export interface CreatedMigration { * @param esClient An {@link ElasticsearchClient} * @param index name of the concrete signals index to be migrated * @param version version of the current signals template/mappings - * @param reindexOptions object containing reindex options {@link SignalsReindexOptions} + * @param reindexOptions object containing reindex options {@link AlertsReindexOptions} * * @returns identifying information representing the {@link MigrationInfo} * @throws if elasticsearch returns an error @@ -37,7 +37,7 @@ export const createMigration = async ({ }: { esClient: ElasticsearchClient; index: string; - reindexOptions: SignalsReindexOptions; + reindexOptions: AlertsReindexOptions; version: number; }): Promise => { const migrationIndex = await createMigrationIndex({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/migrations/migration_service.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/migrations/migration_service.ts index e0c82f70549fd..5a4399bd6389c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/migrations/migration_service.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/migrations/migration_service.ts @@ -6,7 +6,7 @@ */ import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; -import type { SignalsReindexOptions } from '../../../../common/api/detection_engine/signals_migration'; +import type { AlertsReindexOptions } from '../../../../common/api/detection_engine/signals_migration'; import type { SignalsMigrationSO } from './saved_objects_schema'; import { createMigrationSavedObject } from './create_migration_saved_object'; import { createMigration } from './create_migration'; @@ -16,7 +16,7 @@ import { deleteMigration } from './delete_migration'; export interface CreateParams { index: string; version: number; - reindexOptions: SignalsReindexOptions; + reindexOptions: AlertsReindexOptions; } export interface FinalizeParams { 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 82a40cbf71a41..2bd7efdbbdcf2 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 @@ -11,6 +11,12 @@ import { ALERT_WORKFLOW_STATUS } from '@kbn/rule-data-utils'; import { ruleTypeMappings } from '@kbn/securitysolution-rules'; import type { SanitizedRule, ResolvedSanitizedRule } from '@kbn/alerting-plugin/common'; +import type { + SetAlertsStatusByIds, + SetAlertsStatusByQuery, + SetAlertsStatusRequestBodyInput, + SearchAlertsRequestBody, +} from '../../../../../common/api/detection_engine/signals'; import { DETECTION_ENGINE_RULES_URL, DETECTION_ENGINE_SIGNALS_STATUS_URL, @@ -36,10 +42,7 @@ import { } from '../../../../../common/api/detection_engine/rule_management/mocks'; import { getCreateRulesSchemaMock } from '../../../../../common/api/detection_engine/model/rule_schema/mocks'; -import type { - QuerySignalsSchemaDecoded, - SetSignalsStatusSchemaDecoded, -} from '../../../../../common/api/detection_engine/signals'; + import { getFinalizeSignalsMigrationSchemaMock, getSignalsMigrationStatusSchemaMock, @@ -53,26 +56,28 @@ import { getQueryRuleParams } from '../../rule_schema/mocks'; import { requestMock } from './request'; import type { HapiReadableStream } from '../../../../types'; -export const typicalSetStatusSignalByIdsPayload = (): SetSignalsStatusSchemaDecoded => ({ +export const typicalSetStatusSignalByIdsPayload = (): SetAlertsStatusByIds => ({ signal_ids: ['somefakeid1', 'somefakeid2'], status: 'closed', }); -export const typicalSetStatusSignalByQueryPayload = (): SetSignalsStatusSchemaDecoded => ({ +export const typicalSetStatusSignalByQueryPayload = (): SetAlertsStatusByQuery => ({ query: { bool: { filter: { range: { '@timestamp': { gte: 'now-2M', lte: 'now/M' } } } } }, status: 'closed', + conflicts: 'abort', }); -export const typicalSignalsQuery = (): QuerySignalsSchemaDecoded => ({ +export const typicalSignalsQuery = (): SearchAlertsRequestBody => ({ aggs: {}, query: { match_all: {} }, }); -export const typicalSignalsQueryAggs = (): QuerySignalsSchemaDecoded => ({ +export const typicalSignalsQueryAggs = (): SearchAlertsRequestBody => ({ aggs: { statuses: { terms: { field: ALERT_WORKFLOW_STATUS, size: 10 } } }, }); -export const setStatusSignalMissingIdsAndQueryPayload = (): SetSignalsStatusSchemaDecoded => ({ +// @ts-expect-error data with missing required fields +export const setStatusSignalMissingIdsAndQueryPayload = (): SetAlertsStatusRequestBodyInput => ({ status: 'closed', }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.test.ts index e48cd6ff2dab1..5d81134325c50 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.test.ts @@ -7,7 +7,6 @@ import { requestMock, serverMock } from '../__mocks__'; import type { SetupPlugins } from '../../../../plugin'; -import type { SignalsReindexOptions } from '../../../../../common/api/detection_engine/signals_migration'; import { DETECTION_ENGINE_SIGNALS_MIGRATION_URL } from '../../../../../common/constants'; import { getCreateSignalsMigrationSchemaMock } from '../../../../../common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration_route.mock'; import { getIndexVersionsByIndex } from '../../migrations/get_index_versions_by_index'; @@ -17,6 +16,7 @@ import { getIndexAliases } from '@kbn/securitysolution-es-utils'; import { getTemplateVersion } from '../index/check_template_version'; import { createSignalsMigrationRoute } from './create_signals_migration_route'; import { SIGNALS_TEMPLATE_VERSION } from '../index/get_signals_template'; +import type { AlertsReindexOptions } from '../../../../../common/api/detection_engine/signals_migration'; jest.mock('../index/check_template_version'); jest.mock('@kbn/securitysolution-es-utils', () => { @@ -53,7 +53,7 @@ describe('creating signals migrations route', () => { }); it('passes options to the createMigration', async () => { - const reindexOptions: SignalsReindexOptions = { requests_per_second: 4, size: 10, slices: 2 }; + const reindexOptions: AlertsReindexOptions = { requests_per_second: 4, size: 10, slices: 2 }; const request = requestMock.create({ method: 'post', path: DETECTION_ENGINE_SIGNALS_MIGRATION_URL, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.ts index 198a1992058fa..51e1843e9b8e8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/create_signals_migration_route.ts @@ -6,11 +6,11 @@ */ import { transformError, BadRequestError, getIndexAliases } from '@kbn/securitysolution-es-utils'; +import { CreateAlertsMigrationRequestBody } from '../../../../../common/api/detection_engine/signals_migration'; import type { SecuritySolutionPluginRouter } from '../../../../types'; import type { SetupPlugins } from '../../../../plugin'; import { DETECTION_ENGINE_SIGNALS_MIGRATION_URL } from '../../../../../common/constants'; -import { createSignalsMigrationSchema } from '../../../../../common/api/detection_engine/signals_migration'; -import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; +import { buildRouteValidationWithZod } from '../../../../utils/build_validation/route_validation'; import { buildSiemResponse } from '../utils'; import { getTemplateVersion } from '../index/check_template_version'; @@ -35,7 +35,9 @@ export const createSignalsMigrationRoute = ( .addVersion( { version: '2023-10-31', - validate: { request: { body: buildRouteValidation(createSignalsMigrationSchema) } }, + validate: { + request: { body: buildRouteValidationWithZod(CreateAlertsMigrationRequestBody) }, + }, }, async (context, request, response) => { const siemResponse = buildSiemResponse(response); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/delete_signals_migration_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/delete_signals_migration_route.ts index 4a4a38e074c0b..c2e364e58ca5b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/delete_signals_migration_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/delete_signals_migration_route.ts @@ -6,11 +6,11 @@ */ import { transformError } from '@kbn/securitysolution-es-utils'; +import { AlertsMigrationCleanupRequestBody } from '../../../../../common/api/detection_engine/signals_migration'; import type { SecuritySolutionPluginRouter } from '../../../../types'; import type { SetupPlugins } from '../../../../plugin'; import { DETECTION_ENGINE_SIGNALS_MIGRATION_URL } from '../../../../../common/constants'; -import { deleteSignalsMigrationSchema } from '../../../../../common/api/detection_engine/signals_migration'; -import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; +import { buildRouteValidationWithZod } from '../../../../utils/build_validation/route_validation'; import { buildSiemResponse } from '../utils'; import { signalsMigrationService } from '../../migrations/migration_service'; @@ -31,7 +31,9 @@ export const deleteSignalsMigrationRoute = ( .addVersion( { version: '2023-10-31', - validate: { request: { body: buildRouteValidation(deleteSignalsMigrationSchema) } }, + validate: { + request: { body: buildRouteValidationWithZod(AlertsMigrationCleanupRequestBody) }, + }, }, async (context, request, response) => { const siemResponse = buildSiemResponse(response); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.ts index 12d6520fa52d1..bfec0f82867a6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/finalize_signals_migration_route.ts @@ -7,11 +7,11 @@ import { transformError, BadRequestError } from '@kbn/securitysolution-es-utils'; import type { RuleDataPluginService } from '@kbn/rule-registry-plugin/server'; +import { FinalizeAlertsMigrationRequestBody } from '../../../../../common/api/detection_engine/signals_migration'; import type { SecuritySolutionPluginRouter } from '../../../../types'; import type { SetupPlugins } from '../../../../plugin'; import { DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL } from '../../../../../common/constants'; -import { finalizeSignalsMigrationSchema } from '../../../../../common/api/detection_engine/signals_migration'; -import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; +import { buildRouteValidationWithZod } from '../../../../utils/build_validation/route_validation'; import { isMigrationFailed, isMigrationPending } from '../../migrations/helpers'; import { signalsMigrationService } from '../../migrations/migration_service'; import { buildSiemResponse } from '../utils'; @@ -34,7 +34,9 @@ export const finalizeSignalsMigrationRoute = ( .addVersion( { version: '2023-10-31', - validate: { request: { body: buildRouteValidation(finalizeSignalsMigrationSchema) } }, + validate: { + request: { body: buildRouteValidationWithZod(FinalizeAlertsMigrationRequestBody) }, + }, }, async (context, request, response) => { const siemResponse = buildSiemResponse(response); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/get_signals_migration_status_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/get_signals_migration_status_route.ts index dbf6d97d4b72b..185e0c6ed6175 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/get_signals_migration_status_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/get_signals_migration_status_route.ts @@ -6,10 +6,10 @@ */ import { transformError, getIndexAliases } from '@kbn/securitysolution-es-utils'; +import { GetAlertsMigrationStatusRequestQuery } from '../../../../../common/api/detection_engine/signals_migration'; import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DETECTION_ENGINE_SIGNALS_MIGRATION_STATUS_URL } from '../../../../../common/constants'; -import { getSignalsMigrationStatusSchema } from '../../../../../common/api/detection_engine/signals_migration'; -import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; +import { buildRouteValidationWithZod } from '../../../../utils/build_validation/route_validation'; import { getIndexVersionsByIndex } from '../../migrations/get_index_versions_by_index'; import { getMigrationSavedObjectsByIndex } from '../../migrations/get_migration_saved_objects_by_index'; import { getSignalsIndicesInRange } from '../../migrations/get_signals_indices_in_range'; @@ -30,7 +30,9 @@ export const getSignalsMigrationStatusRoute = (router: SecuritySolutionPluginRou .addVersion( { version: '2023-10-31', - validate: { request: { query: buildRouteValidation(getSignalsMigrationStatusSchema) } }, + validate: { + request: { query: buildRouteValidationWithZod(GetAlertsMigrationStatusRequestQuery) }, + }, }, async (context, request, response) => { const siemResponse = buildSiemResponse(response); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals.test.ts index ece3b64ad2fbf..1b2bdb6ca1ef4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals.test.ts @@ -150,12 +150,10 @@ describe('set signal status', () => { path: DETECTION_ENGINE_SIGNALS_STATUS_URL, body: setStatusSignalMissingIdsAndQueryPayload(), }); - const response = await server.inject(request, requestContextMock.convertContext(context)); - expect(response.status).toEqual(400); - expect(response.body).toEqual({ - message: ['either "signal_ids" or "query" must be set'], - status_code: 400, - }); + + const result = server.validate(request); + + expect(result.badRequest).toHaveBeenCalled(); }); test('rejects if signal_ids but no status', async () => { @@ -167,9 +165,7 @@ describe('set signal status', () => { }); const result = server.validate(request); - expect(result.badRequest).toHaveBeenCalledWith( - 'Invalid value "undefined" supplied to "status"' - ); + expect(result.badRequest).toHaveBeenCalled(); }); test('rejects if query but no status', async () => { @@ -181,9 +177,7 @@ describe('set signal status', () => { }); const result = server.validate(request); - expect(result.badRequest).toHaveBeenCalledWith( - 'Invalid value "undefined" supplied to "status"' - ); + expect(result.badRequest).toHaveBeenCalled(); }); test('rejects if query and signal_ids but no status', async () => { @@ -199,9 +193,7 @@ describe('set signal status', () => { }); const result = server.validate(request); - expect(result.badRequest).toHaveBeenCalledWith( - 'Invalid value "undefined" supplied to "status"' - ); + expect(result.badRequest).toHaveBeenCalled(); }); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts index 43a1951bea4c5..3030e5fe799b6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/open_close_signals_route.ts @@ -14,11 +14,8 @@ import { } from '@kbn/rule-data-utils'; import type { ElasticsearchClient, Logger, StartServicesAccessor } from '@kbn/core/server'; import type { AuthenticatedUser } from '@kbn/security-plugin/common'; -import { - setSignalStatusValidateTypeDependents, - setSignalsStatusSchema, -} from '../../../../../common/api/detection_engine/signals'; -import type { SetSignalsStatusSchemaDecoded } from '../../../../../common/api/detection_engine/signals'; +import { buildRouteValidationWithZod } from '../../../../utils/build_validation/route_validation'; +import { SetAlertsStatusRequestBody } from '../../../../../common/api/detection_engine/signals'; import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DEFAULT_ALERTS_INDEX, @@ -28,7 +25,6 @@ import { buildSiemResponse } from '../utils'; import type { ITelemetryEventsSender } from '../../../telemetry/sender'; import { INSIGHTS_CHANNEL } from '../../../telemetry/constants'; import type { StartPlugins } from '../../../../plugin'; -import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; import { getSessionIDfromKibanaRequest, createAlertStatusPayloads, @@ -53,27 +49,19 @@ export const setSignalsStatusRoute = ( version: '2023-10-31', validate: { request: { - body: buildRouteValidation< - typeof setSignalsStatusSchema, - SetSignalsStatusSchemaDecoded - >(setSignalsStatusSchema), + body: buildRouteValidationWithZod(SetAlertsStatusRequestBody), }, }, }, async (context, request, response) => { - const { conflicts, signal_ids: signalIds, query, status } = request.body; + const { status } = request.body; const core = await context.core; const securitySolution = await context.securitySolution; const esClient = core.elasticsearch.client.asCurrentUser; const siemClient = securitySolution?.getAppClient(); const siemResponse = buildSiemResponse(response); - const validationErrors = setSignalStatusValidateTypeDependents(request.body); const spaceId = securitySolution?.getSpaceId() ?? 'default'; - if (validationErrors.length) { - return siemResponse.error({ statusCode: 400, body: validationErrors }); - } - if (!siemClient) { return siemResponse.error({ statusCode: 404 }); } @@ -85,9 +73,13 @@ export const setSignalsStatusRoute = ( sender.isTelemetryOptedIn(), security?.authc.getCurrentUser(request)?.username, ]); + if (isTelemetryOptedIn && clusterId) { // Sometimes the ids are in the query not passed in the request? - const toSendAlertIds = get(query, 'bool.filter.terms._id') || signalIds; + const toSendAlertIds = + 'signal_ids' in request.body + ? request.body.signal_ids + : (get(request.body.query, 'bool.filter.terms._id') as string[]); // Get Context for Insights Payloads const sessionId = getSessionIDfromKibanaRequest(clusterId, request); if (username && toSendAlertIds && sessionId && status) { @@ -105,10 +97,15 @@ export const setSignalsStatusRoute = ( } try { - if (signalIds) { + if ('signal_ids' in request.body) { + const { signal_ids: signalIds } = request.body; + const body = await updateSignalsStatusByIds(status, signalIds, spaceId, esClient, user); + return response.ok({ body }); } else { + const { conflicts, query } = request.body; + const body = await updateSignalsStatusByQuery( status, query, @@ -117,6 +114,7 @@ export const setSignalsStatusRoute = ( esClient, user ); + return response.ok({ body }); } } catch (err) { @@ -132,7 +130,7 @@ export const setSignalsStatusRoute = ( }; const updateSignalsStatusByIds = async ( - status: SetSignalsStatusSchemaDecoded['status'], + status: SetAlertsStatusRequestBody['status'], signalsId: string[], spaceId: string, esClient: ElasticsearchClient, @@ -158,7 +156,7 @@ const updateSignalsStatusByIds = async ( * This method calls `updateByQuery` with `refresh: true` which is expensive on serverless. */ const updateSignalsStatusByQuery = async ( - status: SetSignalsStatusSchemaDecoded['status'], + status: SetAlertsStatusRequestBody['status'], query: object | undefined, options: { conflicts: 'abort' | 'proceed' }, spaceId: string, @@ -181,7 +179,7 @@ const updateSignalsStatusByQuery = async ( }); const getUpdateSignalStatusScript = ( - status: SetSignalsStatusSchemaDecoded['status'], + status: SetAlertsStatusRequestBody['status'], user: AuthenticatedUser | null ) => ({ source: `if (ctx._source['${ALERT_WORKFLOW_STATUS}'] != null && ctx._source['${ALERT_WORKFLOW_STATUS}'] != '${status}') { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.ts index 3bd52ebdaacda..e868c2b979018 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/query_signals_route.ts @@ -8,13 +8,12 @@ import type { MappingRuntimeFields, Sort } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { transformError } from '@kbn/securitysolution-es-utils'; import type { IRuleDataClient } from '@kbn/rule-registry-plugin/server'; +import type { AggregationsAggregationContainer } from '@elastic/elasticsearch/lib/api/types'; +import { SearchAlertsRequestBody } from '../../../../../common/api/detection_engine/signals'; +import { buildRouteValidationWithZod } from '../../../../utils/build_validation/route_validation'; import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DETECTION_ENGINE_QUERY_SIGNALS_URL } from '../../../../../common/constants'; import { buildSiemResponse } from '../utils'; -import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; - -import type { QuerySignalsSchemaDecoded } from '../../../../../common/api/detection_engine/signals'; -import { querySignalsSchema } from '../../../../../common/api/detection_engine/signals'; export const querySignalsRoute = ( router: SecuritySolutionPluginRouter, @@ -33,9 +32,7 @@ export const querySignalsRoute = ( version: '2023-10-31', validate: { request: { - body: buildRouteValidation( - querySignalsSchema - ), + body: buildRouteValidationWithZod(SearchAlertsRequestBody), }, }, }, @@ -67,8 +64,7 @@ export const querySignalsRoute = ( index: indexPattern, body: { query, - // Note: I use a spread operator to please TypeScript with aggs: { ...aggs } - aggs: { ...aggs }, + aggs: aggs as Record, _source, fields, track_total_hits, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/set_alert_tags_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/set_alert_tags_route.test.ts index ab9d79c53fc3f..eaeae10d26471 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/set_alert_tags_route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/set_alert_tags_route.test.ts @@ -75,20 +75,9 @@ describe('setAlertTagsRoute', () => { body: getSetAlertTagsRequestMock(['tag-1'], ['tag-2']), }); - context.core.elasticsearch.client.asCurrentUser.updateByQuery.mockResponse( - getSuccessfulSignalUpdateResponse() - ); - - const response = await server.inject(request, requestContextMock.convertContext(context)); - - context.core.elasticsearch.client.asCurrentUser.updateByQuery.mockRejectedValue( - new Error('Test error') - ); + const result = server.validate(request); - expect(response.body).toEqual({ - message: [`No alert ids were provided`], - status_code: 400, - }); + expect(result.badRequest).toHaveBeenCalled(); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/set_alert_tags_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/set_alert_tags_route.ts index 8b7e81f9bf812..95d722ac0a381 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/set_alert_tags_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals/set_alert_tags_route.ts @@ -7,15 +7,14 @@ import { transformError } from '@kbn/securitysolution-es-utils'; import { uniq } from 'lodash/fp'; -import type { SetAlertTagsRequestBodyDecoded } from '../../../../../common/api/detection_engine/alert_tags'; -import { setAlertTagsRequestBody } from '../../../../../common/api/detection_engine/alert_tags'; +import { ManageAlertTagsRequestBody } from '../../../../../common/api/detection_engine/alert_tags'; import type { SecuritySolutionPluginRouter } from '../../../../types'; import { DEFAULT_ALERTS_INDEX, DETECTION_ENGINE_ALERT_TAGS_URL, } from '../../../../../common/constants'; import { buildSiemResponse } from '../utils'; -import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; +import { buildRouteValidationWithZod } from '../../../../utils/build_validation/route_validation'; import { validateAlertTagsArrays } from './helpers'; export const setAlertTagsRoute = (router: SecuritySolutionPluginRouter) => { @@ -32,10 +31,7 @@ export const setAlertTagsRoute = (router: SecuritySolutionPluginRouter) => { version: '2023-10-31', validate: { request: { - body: buildRouteValidation< - typeof setAlertTagsRequestBody, - SetAlertTagsRequestBodyDecoded - >(setAlertTagsRequestBody), + body: buildRouteValidationWithZod(ManageAlertTagsRequestBody), }, }, }, 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 7650153c53ca3..4bc611e50bba2 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 @@ -19,18 +19,22 @@ import { X_ELASTIC_INTERNAL_ORIGIN_REQUEST, } from '@kbn/core-http-common'; +import { AlertsMigrationCleanupRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/signals_migration/delete_signals_migration/delete_signals_migration.gen'; import { BulkCreateRulesRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/bulk_crud/bulk_create_rules/bulk_create_rules_route.gen'; import { BulkDeleteRulesRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/bulk_crud/bulk_delete_rules/bulk_delete_rules_route.gen'; import { BulkPatchRulesRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/bulk_crud/bulk_patch_rules/bulk_patch_rules_route.gen'; import { BulkUpdateRulesRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/bulk_crud/bulk_update_rules/bulk_update_rules_route.gen'; +import { CreateAlertsMigrationRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/signals_migration/create_signals_migration/create_signals_migration.gen'; import { CreateRuleRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/crud/create_rule/create_rule_route.gen'; import { DeleteRuleRequestQueryInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/crud/delete_rule/delete_rule_route.gen'; import { ExportRulesRequestQueryInput, ExportRulesRequestBodyInput, } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/export_rules/export_rules_route.gen'; +import { FinalizeAlertsMigrationRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/signals_migration/finalize_signals_migration/finalize_signals_migration.gen'; import { FindRulesRequestQueryInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/find_rules/find_rules_route.gen'; import { GetAgentPolicySummaryRequestQueryInput } from '@kbn/security-solution-plugin/common/api/endpoint/policy/policy.gen'; +import { GetAlertsMigrationStatusRequestQueryInput } from '@kbn/security-solution-plugin/common/api/detection_engine/signals_migration/get_signals_migration_status/get_signals_migration_status.gen'; import { GetEndpointSuggestionsRequestParamsInput, GetEndpointSuggestionsRequestBodyInput, @@ -45,13 +49,16 @@ import { GetRuleExecutionResultsRequestParamsInput, } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_monitoring/rule_execution_logs/get_rule_execution_results/get_rule_execution_results_route.gen'; import { ImportRulesRequestQueryInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/import_rules/import_rules_route.gen'; +import { ManageAlertTagsRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/alert_tags/set_alert_tags/set_alert_tags.gen'; import { PatchRuleRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/crud/patch_rule/patch_rule_route.gen'; import { PerformBulkActionRequestQueryInput, PerformBulkActionRequestBodyInput, } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/bulk_actions/bulk_actions_route.gen'; import { ReadRuleRequestQueryInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/crud/read_rule/read_rule_route.gen'; +import { SearchAlertsRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/signals/query_signals/query_signals_route.gen'; import { SetAlertAssigneesRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/alert_assignees/set_alert_assignees_route.gen'; +import { SetAlertsStatusRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/signals/set_signal_status/set_signals_status_route.gen'; import { SuggestUserProfilesRequestQueryInput } from '@kbn/security-solution-plugin/common/api/detection_engine/users/suggest_user_profiles_route.gen'; import { UpdateRuleRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine/rule_management/crud/update_rule/update_rule_route.gen'; import { FtrProviderContext } from '../ftr_provider_context'; @@ -60,6 +67,22 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) const supertest = getService('supertest'); return { + /** + * Migrations favor data integrity over shard size. Consequently, unused or orphaned indices are artifacts of +the migration process. A successful migration will result in both the old and new indices being present. +As such, the old, orphaned index can (and likely should) be deleted. While you can delete these indices manually, +the endpoint accomplishes this task by applying a deletion policy to the relevant index, causing it to be deleted +after 30 days. It also deletes other artifacts specific to the migration implementation. + + */ + alertsMigrationCleanup(props: AlertsMigrationCleanupProps) { + return supertest + .delete('/api/detection_engine/signals/migration') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(props.body as object); + }, /** * Creates new detection rules in bulk. */ @@ -104,6 +127,14 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .send(props.body as object); }, + createAlertsMigration(props: CreateAlertsMigrationProps) { + return supertest + .post('/api/detection_engine/signals/migration') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(props.body as object); + }, /** * Create a single detection rule */ @@ -138,6 +169,20 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) .send(props.body as object) .query(props.query); }, + /** + * The finalization endpoint replaces the original index's alias with the successfully migrated index's alias. +The endpoint is idempotent; therefore, it can safely be used to poll a given migration and, upon completion, +finalize it. + + */ + finalizeAlertsMigration(props: FinalizeAlertsMigrationProps) { + return supertest + .post('/api/detection_engine/signals/finalize_migration') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(props.body as object); + }, /** * Finds rules that match the given query. */ @@ -157,6 +202,14 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .query(props.query); }, + getAlertsMigrationStatus(props: GetAlertsMigrationStatusProps) { + return supertest + .post('/api/detection_engine/signals/migration_status') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .query(props.query); + }, getEndpointSuggestions(props: GetEndpointSuggestionsProps) { return supertest .post(replaceParams('/api/endpoint/suggestions/{suggestion_type}', props.params)) @@ -218,6 +271,14 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); }, + manageAlertTags(props: ManageAlertTagsProps) { + return supertest + .post('/api/detection_engine/signals/tags') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(props.body as object); + }, /** * Patch a single rule */ @@ -259,6 +320,14 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana'); }, + searchAlerts(props: SearchAlertsProps) { + return supertest + .post('/api/detection_engine/signals/search') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(props.body as object); + }, /** * Assigns users to alerts. */ @@ -270,6 +339,14 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .send(props.body as object); }, + setAlertsStatus(props: SetAlertsStatusProps) { + return supertest + .post('/api/detection_engine/signals/status') + .set('kbn-xsrf', 'true') + .set(ELASTIC_HTTP_VERSION_HEADER, '2023-10-31') + .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') + .send(props.body as object); + }, /** * Suggests user profiles. */ @@ -295,6 +372,9 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) }; } +export interface AlertsMigrationCleanupProps { + body: AlertsMigrationCleanupRequestBodyInput; +} export interface BulkCreateRulesProps { body: BulkCreateRulesRequestBodyInput; } @@ -307,6 +387,9 @@ export interface BulkPatchRulesProps { export interface BulkUpdateRulesProps { body: BulkUpdateRulesRequestBodyInput; } +export interface CreateAlertsMigrationProps { + body: CreateAlertsMigrationRequestBodyInput; +} export interface CreateRuleProps { body: CreateRuleRequestBodyInput; } @@ -317,12 +400,18 @@ export interface ExportRulesProps { query: ExportRulesRequestQueryInput; body: ExportRulesRequestBodyInput; } +export interface FinalizeAlertsMigrationProps { + body: FinalizeAlertsMigrationRequestBodyInput; +} export interface FindRulesProps { query: FindRulesRequestQueryInput; } export interface GetAgentPolicySummaryProps { query: GetAgentPolicySummaryRequestQueryInput; } +export interface GetAlertsMigrationStatusProps { + query: GetAlertsMigrationStatusRequestQueryInput; +} export interface GetEndpointSuggestionsProps { params: GetEndpointSuggestionsRequestParamsInput; body: GetEndpointSuggestionsRequestBodyInput; @@ -341,6 +430,9 @@ export interface GetRuleExecutionResultsProps { export interface ImportRulesProps { query: ImportRulesRequestQueryInput; } +export interface ManageAlertTagsProps { + body: ManageAlertTagsRequestBodyInput; +} export interface PatchRuleProps { body: PatchRuleRequestBodyInput; } @@ -351,9 +443,15 @@ export interface PerformBulkActionProps { export interface ReadRuleProps { query: ReadRuleRequestQueryInput; } +export interface SearchAlertsProps { + body: SearchAlertsRequestBodyInput; +} export interface SetAlertAssigneesProps { body: SetAlertAssigneesRequestBodyInput; } +export interface SetAlertsStatusProps { + body: SetAlertsStatusRequestBodyInput; +} export interface SuggestUserProfilesProps { query: SuggestUserProfilesRequestQueryInput; } diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/basic_license_essentials_tier/ess_specific_index_logic/migrations/create_alerts_migrations.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/basic_license_essentials_tier/ess_specific_index_logic/migrations/create_alerts_migrations.ts index 389527a532b40..12058ababe0dc 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/basic_license_essentials_tier/ess_specific_index_logic/migrations/create_alerts_migrations.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/basic_license_essentials_tier/ess_specific_index_logic/migrations/create_alerts_migrations.ts @@ -70,11 +70,14 @@ export default ({ getService }: FtrProviderContext): void => { // Finalize the migration after each test so that the .siem-signals alias gets added to the migrated index - // this allows deleteSignalsIndex to find and delete the migrated index await sleep(5000); // Allow the migration to complete - await supertest - .post(DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL) - .set('kbn-xsrf', 'true') - .send({ migration_ids: createdMigrations.map((m) => m.migration_id) }) - .expect(200); + + if (createdMigrations.length > 0) { + await supertest + .post(DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL) + .set('kbn-xsrf', 'true') + .send({ migration_ids: createdMigrations.map((m) => m.migration_id) }) + .expect(200); + } await esArchiver.unload('x-pack/test/functional/es_archives/signals/outdated_signals_index'); await esArchiver.unload('x-pack/test/functional/es_archives/signals/legacy_signals_index'); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/basic_license_essentials_tier/field_aliases.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/basic_license_essentials_tier/field_aliases.ts index b6c6d265c14da..732bb54385a8a 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/basic_license_essentials_tier/field_aliases.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/basic_license_essentials_tier/field_aliases.ts @@ -39,12 +39,9 @@ export default ({ getService }: FtrProviderContext) => { }); beforeEach(async () => { - await createAlertsIndex(supertest, log); - }); - - afterEach(async () => { await deleteAllAlerts(supertest, log, es); await deleteAllRules(supertest, log); + await createAlertsIndex(supertest, log); }); it('should keep the original alias value such as "host_alias" from a source index when the value is indexed', async () => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/basic_license_essentials_tier/query_alerts.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/basic_license_essentials_tier/query_alerts.ts index 95764a6894fd4..63875d58f5d90 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/basic_license_essentials_tier/query_alerts.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/basic_license_essentials_tier/query_alerts.ts @@ -35,6 +35,10 @@ export default ({ getService }: FtrProviderContext) => { const es = getService('es'); describe('@ess @serverless @serverlessQA query_signals_route and find_alerts_route', () => { + beforeEach(async () => { + await deleteAllAlerts(supertest, log, es); + }); + describe('validation checks', () => { it('should not give errors when querying and the alerts index does exist and is empty', async () => { await createAlertsIndex(supertest, log); @@ -61,7 +65,7 @@ export default ({ getService }: FtrProviderContext) => { }); describe('runtime fields', () => { - before(async () => { + beforeEach(async () => { await esArchiver.load( 'x-pack/test/functional/es_archives/security_solution/alerts/8.8.0_multiple_docs', { @@ -71,7 +75,7 @@ export default ({ getService }: FtrProviderContext) => { ); await createAlertsIndex(supertest, log); }); - after(async () => { + afterEach(async () => { await deleteAllAlerts(supertest, log, es); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/basic_license_essentials_tier/set_alert_tags.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/basic_license_essentials_tier/set_alert_tags.ts index e0b09a111ae0c..961150d0908c2 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/basic_license_essentials_tier/set_alert_tags.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/detection_engine/alerts/basic_license_essentials_tier/set_alert_tags.ts @@ -5,7 +5,7 @@ * 2.0. */ -import expect from '@kbn/expect'; +import expect from 'expect'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { @@ -49,9 +49,10 @@ export default ({ getService }: FtrProviderContext) => { .send(setAlertTags({ tagsToAdd: [], tagsToRemove: [], ids: [] })) .expect(400); - expect(body).to.eql({ - message: ['No alert ids were provided'], - status_code: 400, + expect(body).toEqual({ + error: 'Bad Request', + message: '[request body]: ids: Array must contain at least 1 element(s)', + statusCode: 400, }); }); @@ -62,7 +63,7 @@ export default ({ getService }: FtrProviderContext) => { .send(setAlertTags({ tagsToAdd: ['test-1'], tagsToRemove: ['test-1'], ids: ['123'] })) .expect(400); - expect(body).to.eql({ + expect(body).toEqual({ message: [ 'Duplicate tags ["test-1"] were found in the tags_to_add and tags_to_remove parameters.', ], @@ -119,7 +120,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); body.hits.hits.map((alert) => { - expect(alert._source?.['kibana.alert.workflow_tags']).to.eql(['tag-1']); + expect(alert._source?.['kibana.alert.workflow_tags']).toEqual(['tag-1']); }); }); @@ -165,7 +166,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); body.hits.hits.map((alert) => { - expect(alert._source?.['kibana.alert.workflow_tags']).to.eql(['tag-1']); + expect(alert._source?.['kibana.alert.workflow_tags']).toEqual(['tag-1']); }); }); @@ -211,7 +212,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); body.hits.hits.map((alert) => { - expect(alert._source?.['kibana.alert.workflow_tags']).to.eql(['tag-1']); + expect(alert._source?.['kibana.alert.workflow_tags']).toEqual(['tag-1']); }); }); @@ -245,7 +246,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(200); body.hits.hits.map((alert) => { - expect(alert._source?.['kibana.alert.workflow_tags']).to.eql([]); + expect(alert._source?.['kibana.alert.workflow_tags']).toEqual([]); }); }); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/set_alert_tags.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/set_alert_tags.ts index b3ae1d4de0b5c..f826629741179 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/set_alert_tags.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/utils/alerts/set_alert_tags.ts @@ -6,7 +6,7 @@ */ import { AlertTagIds } from '@kbn/security-solution-plugin/common/api/detection_engine'; -import { SetAlertTagsRequestBody } from '@kbn/security-solution-plugin/common/api/detection_engine'; +import { ManageAlertTagsRequestBodyInput } from '@kbn/security-solution-plugin/common/api/detection_engine'; export const setAlertTags = ({ tagsToAdd, @@ -16,7 +16,7 @@ export const setAlertTags = ({ tagsToAdd: string[]; tagsToRemove: string[]; ids: AlertTagIds; -}): SetAlertTagsRequestBody => ({ +}): ManageAlertTagsRequestBodyInput => ({ tags: { tags_to_add: tagsToAdd, tags_to_remove: tagsToRemove,