diff --git a/packages/kbn-securitysolution-exceptions-common/api/create_exception_list/create_exception_list.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/create_exception_list/create_exception_list.schema.yaml index 463009b233af1..5925d0bd923c0 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/create_exception_list/create_exception_list.schema.yaml +++ b/packages/kbn-securitysolution-exceptions-common/api/create_exception_list/create_exception_list.schema.yaml @@ -8,7 +8,11 @@ paths: x-labels: [serverless, ess] operationId: CreateExceptionList x-codegen-enabled: true - summary: Creates an exception list + summary: Create an exception list + description: | + An exception list groups exception items and can be associated with detection rules. You can assign detection rules with multiple exception lists. + > info + > All exception items added to the same list are evaluated using `OR` logic. That is, if any of the items in a list evaluate to `true`, the exception prevents the rule from generating an alert. Likewise, `OR` logic is used for evaluating exceptions when more than one exception list is assigned to a rule. To use the `AND` operator, you can define multiple clauses (`entries`) in a single exception item. requestBody: description: Exception list's properties required: true diff --git a/packages/kbn-securitysolution-exceptions-common/api/create_exception_list_item/create_exception_list_item.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/create_exception_list_item/create_exception_list_item.schema.yaml index f7eb416f953a6..47fa2895d27c6 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/create_exception_list_item/create_exception_list_item.schema.yaml +++ b/packages/kbn-securitysolution-exceptions-common/api/create_exception_list_item/create_exception_list_item.schema.yaml @@ -8,7 +8,11 @@ paths: x-labels: [serverless, ess] operationId: CreateExceptionListItem x-codegen-enabled: true - summary: Creates an exception list item + summary: Create an exception list item + description: | + Create an exception item and associate it with the specified exception list. + > info + > Before creating exception items, you must create an exception list. requestBody: description: Exception list item's properties required: true diff --git a/packages/kbn-securitysolution-exceptions-common/api/create_rule_exceptions/create_rule_exceptions.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/create_rule_exceptions/create_rule_exceptions.schema.yaml index 0928d9e7f4e21..6162d00d78ae8 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/create_rule_exceptions/create_rule_exceptions.schema.yaml +++ b/packages/kbn-securitysolution-exceptions-common/api/create_rule_exceptions/create_rule_exceptions.schema.yaml @@ -8,7 +8,8 @@ paths: x-labels: [serverless, ess] operationId: CreateRuleExceptionListItems x-codegen-enabled: true - summary: Creates rule exception list items + summary: Create rule exception list items + description: Create exception items that apply to a single detection rule. parameters: - name: id in: path diff --git a/packages/kbn-securitysolution-exceptions-common/api/create_shared_exceptions_list/create_shared_exceptions_list.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/create_shared_exceptions_list/create_shared_exceptions_list.schema.yaml index e76ec4c50c5c8..c4cee089e5836 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/create_shared_exceptions_list/create_shared_exceptions_list.schema.yaml +++ b/packages/kbn-securitysolution-exceptions-common/api/create_shared_exceptions_list/create_shared_exceptions_list.schema.yaml @@ -8,7 +8,11 @@ paths: x-labels: [serverless, ess] operationId: CreateSharedExceptionList x-codegen-enabled: true - summary: Creates a shared exception list + summary: Create a shared exception list + description: | + An exception list groups exception items and can be associated with detection rules. A shared exception list can apply to multiple detection rules. + > info + > All exception items added to the same list are evaluated using `OR` logic. That is, if any of the items in a list evaluate to `true`, the exception prevents the rule from generating an alert. Likewise, `OR` logic is used for evaluating exceptions when more than one exception list is assigned to a rule. To use the `AND` operator, you can define multiple clauses (`entries`) in a single exception item. requestBody: required: true content: diff --git a/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list/delete_exception_list.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list/delete_exception_list.schema.yaml index ccc0749dd206e..92afc3232efee 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list/delete_exception_list.schema.yaml +++ b/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list/delete_exception_list.schema.yaml @@ -8,7 +8,8 @@ paths: x-labels: [serverless, ess] operationId: DeleteExceptionList x-codegen-enabled: true - summary: Deletes an exception list + summary: Delete an exception list + description: Delete an exception list using the `id` or `list_id` field. parameters: - name: id in: query diff --git a/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list_item/delete_exception_list_item.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list_item/delete_exception_list_item.schema.yaml index e9d7fa0687044..9f57afcd5ab1c 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list_item/delete_exception_list_item.schema.yaml +++ b/packages/kbn-securitysolution-exceptions-common/api/delete_exception_list_item/delete_exception_list_item.schema.yaml @@ -8,7 +8,8 @@ paths: x-labels: [serverless, ess] operationId: DeleteExceptionListItem x-codegen-enabled: true - summary: Deletes an exception list item + summary: Delete an exception list item + description: Delete an exception list item using the `id` or `item_id` field. parameters: - name: id in: query diff --git a/packages/kbn-securitysolution-exceptions-common/api/duplicate_exception_list/duplicate_exception_list.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/duplicate_exception_list/duplicate_exception_list.schema.yaml index f041fcb1d1062..758171327ee4c 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/duplicate_exception_list/duplicate_exception_list.schema.yaml +++ b/packages/kbn-securitysolution-exceptions-common/api/duplicate_exception_list/duplicate_exception_list.schema.yaml @@ -8,7 +8,8 @@ paths: x-labels: [serverless, ess] operationId: DuplicateExceptionList x-codegen-enabled: true - summary: Duplicates an exception list + summary: Duplicate an exception list + description: Duplicate an existing exception list. parameters: - name: list_id in: query diff --git a/packages/kbn-securitysolution-exceptions-common/api/export_exception_list/export_exception_list.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/export_exception_list/export_exception_list.schema.yaml index 41637963d9923..3232f46c238c8 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/export_exception_list/export_exception_list.schema.yaml +++ b/packages/kbn-securitysolution-exceptions-common/api/export_exception_list/export_exception_list.schema.yaml @@ -8,8 +8,8 @@ paths: x-labels: [serverless, ess] operationId: ExportExceptionList x-codegen-enabled: true - summary: Exports an exception list - description: Exports an exception list and its associated items to an .ndjson file + summary: Export an exception list + description: Export an exception list and its associated items to an NDJSON file. parameters: - name: id in: query diff --git a/packages/kbn-securitysolution-exceptions-common/api/find_exception_list_items/find_exception_list_items.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/find_exception_list_items/find_exception_list_items.schema.yaml index f2b3aafdee107..e40f780af03ef 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/find_exception_list_items/find_exception_list_items.schema.yaml +++ b/packages/kbn-securitysolution-exceptions-common/api/find_exception_list_items/find_exception_list_items.schema.yaml @@ -8,7 +8,8 @@ paths: x-labels: [serverless, ess] operationId: FindExceptionListItems x-codegen-enabled: true - summary: Finds exception list items + summary: Get exception list items + description: Get a list of all exception list items in the specified list. parameters: - name: list_id in: query diff --git a/packages/kbn-securitysolution-exceptions-common/api/find_exception_lists/find_exception_lists.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/find_exception_lists/find_exception_lists.schema.yaml index a1df1d12a27ea..c46dacbab01d0 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/find_exception_lists/find_exception_lists.schema.yaml +++ b/packages/kbn-securitysolution-exceptions-common/api/find_exception_lists/find_exception_lists.schema.yaml @@ -8,7 +8,8 @@ paths: x-labels: [serverless, ess] operationId: FindExceptionLists x-codegen-enabled: true - summary: Finds exception lists + summary: Get exception lists + description: Get a list of all exception lists. parameters: - name: filter in: query diff --git a/packages/kbn-securitysolution-exceptions-common/api/import_exceptions/import_exceptions.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/import_exceptions/import_exceptions.schema.yaml index dc67d1386475f..8ae3ac1aa2c0c 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/import_exceptions/import_exceptions.schema.yaml +++ b/packages/kbn-securitysolution-exceptions-common/api/import_exceptions/import_exceptions.schema.yaml @@ -8,8 +8,8 @@ paths: x-labels: [serverless, ess] operationId: ImportExceptionList x-codegen-enabled: true - summary: Imports an exception list - description: Imports an exception list and associated items + summary: Import an exception list + description: Import an exception list and its associated items from an NDJSON file. requestBody: required: true content: diff --git a/packages/kbn-securitysolution-exceptions-common/api/quickstart_client.gen.ts b/packages/kbn-securitysolution-exceptions-common/api/quickstart_client.gen.ts index 52e11f1ea4033..4827baab85e90 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/quickstart_client.gen.ts +++ b/packages/kbn-securitysolution-exceptions-common/api/quickstart_client.gen.ts @@ -98,6 +98,12 @@ export class Client { this.kbnClient = options.kbnClient; this.log = options.log; } + /** + * An exception list groups exception items and can be associated with detection rules. You can assign detection rules with multiple exception lists. +> info +> All exception items added to the same list are evaluated using `OR` logic. That is, if any of the items in a list evaluate to `true`, the exception prevents the rule from generating an alert. Likewise, `OR` logic is used for evaluating exceptions when more than one exception list is assigned to a rule. To use the `AND` operator, you can define multiple clauses (`entries`) in a single exception item. + + */ async createExceptionList(props: CreateExceptionListProps) { this.log.info(`${new Date().toISOString()} Calling API CreateExceptionList`); return this.kbnClient @@ -111,6 +117,12 @@ export class Client { }) .catch(catchAxiosErrorFormatAndThrow); } + /** + * Create an exception item and associate it with the specified exception list. +> info +> Before creating exception items, you must create an exception list. + + */ async createExceptionListItem(props: CreateExceptionListItemProps) { this.log.info(`${new Date().toISOString()} Calling API CreateExceptionListItem`); return this.kbnClient @@ -124,6 +136,9 @@ export class Client { }) .catch(catchAxiosErrorFormatAndThrow); } + /** + * Create exception items that apply to a single detection rule. + */ async createRuleExceptionListItems(props: CreateRuleExceptionListItemsProps) { this.log.info(`${new Date().toISOString()} Calling API CreateRuleExceptionListItems`); return this.kbnClient @@ -137,6 +152,12 @@ export class Client { }) .catch(catchAxiosErrorFormatAndThrow); } + /** + * An exception list groups exception items and can be associated with detection rules. A shared exception list can apply to multiple detection rules. +> info +> All exception items added to the same list are evaluated using `OR` logic. That is, if any of the items in a list evaluate to `true`, the exception prevents the rule from generating an alert. Likewise, `OR` logic is used for evaluating exceptions when more than one exception list is assigned to a rule. To use the `AND` operator, you can define multiple clauses (`entries`) in a single exception item. + + */ async createSharedExceptionList(props: CreateSharedExceptionListProps) { this.log.info(`${new Date().toISOString()} Calling API CreateSharedExceptionList`); return this.kbnClient @@ -150,6 +171,9 @@ export class Client { }) .catch(catchAxiosErrorFormatAndThrow); } + /** + * Delete an exception list using the `id` or `list_id` field. + */ async deleteExceptionList(props: DeleteExceptionListProps) { this.log.info(`${new Date().toISOString()} Calling API DeleteExceptionList`); return this.kbnClient @@ -164,6 +188,9 @@ export class Client { }) .catch(catchAxiosErrorFormatAndThrow); } + /** + * Delete an exception list item using the `id` or `item_id` field. + */ async deleteExceptionListItem(props: DeleteExceptionListItemProps) { this.log.info(`${new Date().toISOString()} Calling API DeleteExceptionListItem`); return this.kbnClient @@ -178,6 +205,9 @@ export class Client { }) .catch(catchAxiosErrorFormatAndThrow); } + /** + * Duplicate an existing exception list. + */ async duplicateExceptionList(props: DuplicateExceptionListProps) { this.log.info(`${new Date().toISOString()} Calling API DuplicateExceptionList`); return this.kbnClient @@ -193,7 +223,7 @@ export class Client { .catch(catchAxiosErrorFormatAndThrow); } /** - * Exports an exception list and its associated items to an .ndjson file + * Export an exception list and its associated items to an NDJSON file. */ async exportExceptionList(props: ExportExceptionListProps) { this.log.info(`${new Date().toISOString()} Calling API ExportExceptionList`); @@ -209,6 +239,9 @@ export class Client { }) .catch(catchAxiosErrorFormatAndThrow); } + /** + * Get a list of all exception list items in the specified list. + */ async findExceptionListItems(props: FindExceptionListItemsProps) { this.log.info(`${new Date().toISOString()} Calling API FindExceptionListItems`); return this.kbnClient @@ -223,6 +256,9 @@ export class Client { }) .catch(catchAxiosErrorFormatAndThrow); } + /** + * Get a list of all exception lists. + */ async findExceptionLists(props: FindExceptionListsProps) { this.log.info(`${new Date().toISOString()} Calling API FindExceptionLists`); return this.kbnClient @@ -238,7 +274,7 @@ export class Client { .catch(catchAxiosErrorFormatAndThrow); } /** - * Imports an exception list and associated items + * Import an exception list and its associated items from an NDJSON file. */ async importExceptionList(props: ImportExceptionListProps) { this.log.info(`${new Date().toISOString()} Calling API ImportExceptionList`); @@ -254,6 +290,9 @@ export class Client { }) .catch(catchAxiosErrorFormatAndThrow); } + /** + * Get the details of an exception list using the `id` or `list_id` field. + */ async readExceptionList(props: ReadExceptionListProps) { this.log.info(`${new Date().toISOString()} Calling API ReadExceptionList`); return this.kbnClient @@ -268,6 +307,9 @@ export class Client { }) .catch(catchAxiosErrorFormatAndThrow); } + /** + * Get the details of an exception list item using the `id` or `item_id` field. + */ async readExceptionListItem(props: ReadExceptionListItemProps) { this.log.info(`${new Date().toISOString()} Calling API ReadExceptionListItem`); return this.kbnClient @@ -282,6 +324,9 @@ export class Client { }) .catch(catchAxiosErrorFormatAndThrow); } + /** + * Get a summary of the specified exception list. + */ async readExceptionListSummary(props: ReadExceptionListSummaryProps) { this.log.info(`${new Date().toISOString()} Calling API ReadExceptionListSummary`); return this.kbnClient @@ -296,6 +341,9 @@ export class Client { }) .catch(catchAxiosErrorFormatAndThrow); } + /** + * Update an exception list using the `id` or `list_id` field. + */ async updateExceptionList(props: UpdateExceptionListProps) { this.log.info(`${new Date().toISOString()} Calling API UpdateExceptionList`); return this.kbnClient @@ -309,6 +357,9 @@ export class Client { }) .catch(catchAxiosErrorFormatAndThrow); } + /** + * Update an exception list item using the `id` or `item_id` field. + */ async updateExceptionListItem(props: UpdateExceptionListItemProps) { this.log.info(`${new Date().toISOString()} Calling API UpdateExceptionListItem`); return this.kbnClient diff --git a/packages/kbn-securitysolution-exceptions-common/api/read_exception_list/read_exception_list.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/read_exception_list/read_exception_list.schema.yaml index 69f5b4a9a8aa2..0bf082c1713bd 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/read_exception_list/read_exception_list.schema.yaml +++ b/packages/kbn-securitysolution-exceptions-common/api/read_exception_list/read_exception_list.schema.yaml @@ -8,7 +8,8 @@ paths: x-labels: [serverless, ess] operationId: ReadExceptionList x-codegen-enabled: true - summary: Retrieves an exception list using its `id` or `list_id` field + summary: Get exception list details + description: Get the details of an exception list using the `id` or `list_id` field. parameters: - name: id in: query diff --git a/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_item/read_exception_list_item.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_item/read_exception_list_item.schema.yaml index 9cc6b79b91e63..c271016a87eb5 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_item/read_exception_list_item.schema.yaml +++ b/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_item/read_exception_list_item.schema.yaml @@ -8,7 +8,8 @@ paths: x-labels: [serverless, ess] operationId: ReadExceptionListItem x-codegen-enabled: true - summary: Gets an exception list item + summary: Get an exception list item + description: Get the details of an exception list item using the `id` or `item_id` field. parameters: - name: id in: query diff --git a/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_summary/read_exception_list_summary.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_summary/read_exception_list_summary.schema.yaml index bae534bf3260b..b0627111e877f 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_summary/read_exception_list_summary.schema.yaml +++ b/packages/kbn-securitysolution-exceptions-common/api/read_exception_list_summary/read_exception_list_summary.schema.yaml @@ -8,7 +8,8 @@ paths: x-labels: [serverless, ess] operationId: ReadExceptionListSummary x-codegen-enabled: true - summary: Retrieves an exception list summary + summary: Get an exception list summary + description: Get a summary of the specified exception list. parameters: - name: id in: query diff --git a/packages/kbn-securitysolution-exceptions-common/api/update_exception_list/update_exception_list.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/update_exception_list/update_exception_list.schema.yaml index a58caeb465428..5e8f3dfd8b509 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/update_exception_list/update_exception_list.schema.yaml +++ b/packages/kbn-securitysolution-exceptions-common/api/update_exception_list/update_exception_list.schema.yaml @@ -8,7 +8,8 @@ paths: x-labels: [serverless, ess] operationId: UpdateExceptionList x-codegen-enabled: true - summary: Updates an exception list + summary: Update an exception list + description: Update an exception list using the `id` or `list_id` field. requestBody: description: Exception list's properties required: true diff --git a/packages/kbn-securitysolution-exceptions-common/api/update_exception_list_item/update_exception_list_item.schema.yaml b/packages/kbn-securitysolution-exceptions-common/api/update_exception_list_item/update_exception_list_item.schema.yaml index 180d4865f887b..2b8182aeb5c34 100644 --- a/packages/kbn-securitysolution-exceptions-common/api/update_exception_list_item/update_exception_list_item.schema.yaml +++ b/packages/kbn-securitysolution-exceptions-common/api/update_exception_list_item/update_exception_list_item.schema.yaml @@ -8,7 +8,8 @@ paths: x-labels: [serverless, ess] operationId: UpdateExceptionListItem x-codegen-enabled: true - summary: Updates an exception list item + summary: Update an exception list item + description: Update an exception list item using the `id` or `item_id` field. requestBody: description: Exception list item's properties required: true diff --git a/packages/kbn-securitysolution-exceptions-common/docs/openapi/ess/security_solution_exceptions_api_2023_10_31.bundled.schema.yaml b/packages/kbn-securitysolution-exceptions-common/docs/openapi/ess/security_solution_exceptions_api_2023_10_31.bundled.schema.yaml index 8acc028520f11..bf0c2502ae80f 100644 --- a/packages/kbn-securitysolution-exceptions-common/docs/openapi/ess/security_solution_exceptions_api_2023_10_31.bundled.schema.yaml +++ b/packages/kbn-securitysolution-exceptions-common/docs/openapi/ess/security_solution_exceptions_api_2023_10_31.bundled.schema.yaml @@ -16,6 +16,7 @@ servers: paths: '/api/detection_engine/rules/{id}/exceptions': post: + description: Create exception items that apply to a single detection rule. operationId: CreateRuleExceptionListItems parameters: - description: Detection rule's identifier @@ -73,11 +74,12 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Creates rule exception list items + summary: Create rule exception list items tags: - Security Solution Exceptions API /api/exception_lists: delete: + description: Delete an exception list using the `id` or `list_id` field. operationId: DeleteExceptionList parameters: - description: Either `id` or `list_id` must be specified @@ -137,10 +139,11 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Deletes an exception list + summary: Delete an exception list tags: - Security Solution Exceptions API get: + description: Get the details of an exception list using the `id` or `list_id` field. operationId: ReadExceptionList parameters: - description: Either `id` or `list_id` must be specified @@ -200,10 +203,23 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Retrieves an exception list using its `id` or `list_id` field + summary: Get exception list details tags: - Security Solution Exceptions API post: + description: > + An exception list groups exception items and can be associated with + detection rules. You can assign detection rules with multiple exception + lists. + + > info + + > All exception items added to the same list are evaluated using `OR` + logic. That is, if any of the items in a list evaluate to `true`, the + exception prevents the rule from generating an alert. Likewise, `OR` + logic is used for evaluating exceptions when more than one exception + list is assigned to a rule. To use the `AND` operator, you can define + multiple clauses (`entries`) in a single exception item. operationId: CreateExceptionList requestBody: content: @@ -277,10 +293,11 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Creates an exception list + summary: Create an exception list tags: - Security Solution Exceptions API put: + description: Update an exception list using the `id` or `list_id` field. operationId: UpdateExceptionList requestBody: content: @@ -357,11 +374,12 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Updates an exception list + summary: Update an exception list tags: - Security Solution Exceptions API /api/exception_lists/_duplicate: post: + description: Duplicate an existing exception list. operationId: DuplicateExceptionList parameters: - description: Exception list's human identifier @@ -426,12 +444,12 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Duplicates an exception list + summary: Duplicate an exception list tags: - Security Solution Exceptions API /api/exception_lists/_export: post: - description: Exports an exception list and its associated items to an .ndjson file + description: Export an exception list and its associated items to an NDJSON file. operationId: ExportExceptionList parameters: - description: Exception list's identifier @@ -506,11 +524,12 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Exports an exception list + summary: Export an exception list tags: - Security Solution Exceptions API /api/exception_lists/_find: get: + description: Get a list of all exception lists. operationId: FindExceptionLists parameters: - description: > @@ -626,12 +645,12 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Finds exception lists + summary: Get exception lists tags: - Security Solution Exceptions API /api/exception_lists/_import: post: - description: Imports an exception list and associated items + description: Import an exception list and its associated items from an NDJSON file. operationId: ImportExceptionList parameters: - description: > @@ -742,11 +761,12 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Imports an exception list + summary: Import an exception list tags: - Security Solution Exceptions API /api/exception_lists/items: delete: + description: Delete an exception list item using the `id` or `item_id` field. operationId: DeleteExceptionListItem parameters: - description: Either `id` or `item_id` must be specified @@ -806,10 +826,13 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Deletes an exception list item + summary: Delete an exception list item tags: - Security Solution Exceptions API get: + description: >- + Get the details of an exception list item using the `id` or `item_id` + field. operationId: ReadExceptionListItem parameters: - description: Either `id` or `item_id` must be specified @@ -869,10 +892,17 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Gets an exception list item + summary: Get an exception list item tags: - Security Solution Exceptions API post: + description: > + Create an exception item and associate it with the specified exception + list. + + > info + + > Before creating exception items, you must create an exception list. operationId: CreateExceptionListItem requestBody: content: @@ -956,10 +986,11 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Creates an exception list item + summary: Create an exception list item tags: - Security Solution Exceptions API put: + description: Update an exception list item using the `id` or `item_id` field. operationId: UpdateExceptionListItem requestBody: content: @@ -1047,11 +1078,12 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Updates an exception list item + summary: Update an exception list item tags: - Security Solution Exceptions API /api/exception_lists/items/_find: get: + description: Get a list of all exception list items in the specified list. operationId: FindExceptionListItems parameters: - description: List's id @@ -1183,11 +1215,12 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Finds exception list items + summary: Get exception list items tags: - Security Solution Exceptions API /api/exception_lists/summary: get: + description: Get a summary of the specified exception list. operationId: ReadExceptionListSummary parameters: - description: Exception list's identifier generated upon creation @@ -1266,11 +1299,24 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Retrieves an exception list summary + summary: Get an exception list summary tags: - Security Solution Exceptions API /api/exceptions/shared: post: + description: > + An exception list groups exception items and can be associated with + detection rules. A shared exception list can apply to multiple detection + rules. + + > info + + > All exception items added to the same list are evaluated using `OR` + logic. That is, if any of the items in a list evaluate to `true`, the + exception prevents the rule from generating an alert. Likewise, `OR` + logic is used for evaluating exceptions when more than one exception + list is assigned to a rule. To use the `AND` operator, you can define + multiple clauses (`entries`) in a single exception item. operationId: CreateSharedExceptionList requestBody: content: @@ -1325,7 +1371,7 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Creates a shared exception list + summary: Create a shared exception list tags: - Security Solution Exceptions API components: diff --git a/packages/kbn-securitysolution-exceptions-common/docs/openapi/serverless/security_solution_exceptions_api_2023_10_31.bundled.schema.yaml b/packages/kbn-securitysolution-exceptions-common/docs/openapi/serverless/security_solution_exceptions_api_2023_10_31.bundled.schema.yaml index cd104baacda20..2a9bb3996a915 100644 --- a/packages/kbn-securitysolution-exceptions-common/docs/openapi/serverless/security_solution_exceptions_api_2023_10_31.bundled.schema.yaml +++ b/packages/kbn-securitysolution-exceptions-common/docs/openapi/serverless/security_solution_exceptions_api_2023_10_31.bundled.schema.yaml @@ -16,6 +16,7 @@ servers: paths: '/api/detection_engine/rules/{id}/exceptions': post: + description: Create exception items that apply to a single detection rule. operationId: CreateRuleExceptionListItems parameters: - description: Detection rule's identifier @@ -73,11 +74,12 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Creates rule exception list items + summary: Create rule exception list items tags: - Security Solution Exceptions API /api/exception_lists: delete: + description: Delete an exception list using the `id` or `list_id` field. operationId: DeleteExceptionList parameters: - description: Either `id` or `list_id` must be specified @@ -137,10 +139,11 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Deletes an exception list + summary: Delete an exception list tags: - Security Solution Exceptions API get: + description: Get the details of an exception list using the `id` or `list_id` field. operationId: ReadExceptionList parameters: - description: Either `id` or `list_id` must be specified @@ -200,10 +203,23 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Retrieves an exception list using its `id` or `list_id` field + summary: Get exception list details tags: - Security Solution Exceptions API post: + description: > + An exception list groups exception items and can be associated with + detection rules. You can assign detection rules with multiple exception + lists. + + > info + + > All exception items added to the same list are evaluated using `OR` + logic. That is, if any of the items in a list evaluate to `true`, the + exception prevents the rule from generating an alert. Likewise, `OR` + logic is used for evaluating exceptions when more than one exception + list is assigned to a rule. To use the `AND` operator, you can define + multiple clauses (`entries`) in a single exception item. operationId: CreateExceptionList requestBody: content: @@ -277,10 +293,11 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Creates an exception list + summary: Create an exception list tags: - Security Solution Exceptions API put: + description: Update an exception list using the `id` or `list_id` field. operationId: UpdateExceptionList requestBody: content: @@ -357,11 +374,12 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Updates an exception list + summary: Update an exception list tags: - Security Solution Exceptions API /api/exception_lists/_duplicate: post: + description: Duplicate an existing exception list. operationId: DuplicateExceptionList parameters: - description: Exception list's human identifier @@ -426,12 +444,12 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Duplicates an exception list + summary: Duplicate an exception list tags: - Security Solution Exceptions API /api/exception_lists/_export: post: - description: Exports an exception list and its associated items to an .ndjson file + description: Export an exception list and its associated items to an NDJSON file. operationId: ExportExceptionList parameters: - description: Exception list's identifier @@ -506,11 +524,12 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Exports an exception list + summary: Export an exception list tags: - Security Solution Exceptions API /api/exception_lists/_find: get: + description: Get a list of all exception lists. operationId: FindExceptionLists parameters: - description: > @@ -626,12 +645,12 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Finds exception lists + summary: Get exception lists tags: - Security Solution Exceptions API /api/exception_lists/_import: post: - description: Imports an exception list and associated items + description: Import an exception list and its associated items from an NDJSON file. operationId: ImportExceptionList parameters: - description: > @@ -742,11 +761,12 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Imports an exception list + summary: Import an exception list tags: - Security Solution Exceptions API /api/exception_lists/items: delete: + description: Delete an exception list item using the `id` or `item_id` field. operationId: DeleteExceptionListItem parameters: - description: Either `id` or `item_id` must be specified @@ -806,10 +826,13 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Deletes an exception list item + summary: Delete an exception list item tags: - Security Solution Exceptions API get: + description: >- + Get the details of an exception list item using the `id` or `item_id` + field. operationId: ReadExceptionListItem parameters: - description: Either `id` or `item_id` must be specified @@ -869,10 +892,17 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Gets an exception list item + summary: Get an exception list item tags: - Security Solution Exceptions API post: + description: > + Create an exception item and associate it with the specified exception + list. + + > info + + > Before creating exception items, you must create an exception list. operationId: CreateExceptionListItem requestBody: content: @@ -956,10 +986,11 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Creates an exception list item + summary: Create an exception list item tags: - Security Solution Exceptions API put: + description: Update an exception list item using the `id` or `item_id` field. operationId: UpdateExceptionListItem requestBody: content: @@ -1047,11 +1078,12 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Updates an exception list item + summary: Update an exception list item tags: - Security Solution Exceptions API /api/exception_lists/items/_find: get: + description: Get a list of all exception list items in the specified list. operationId: FindExceptionListItems parameters: - description: List's id @@ -1183,11 +1215,12 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Finds exception list items + summary: Get exception list items tags: - Security Solution Exceptions API /api/exception_lists/summary: get: + description: Get a summary of the specified exception list. operationId: ReadExceptionListSummary parameters: - description: Exception list's identifier generated upon creation @@ -1266,11 +1299,24 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Retrieves an exception list summary + summary: Get an exception list summary tags: - Security Solution Exceptions API /api/exceptions/shared: post: + description: > + An exception list groups exception items and can be associated with + detection rules. A shared exception list can apply to multiple detection + rules. + + > info + + > All exception items added to the same list are evaluated using `OR` + logic. That is, if any of the items in a list evaluate to `true`, the + exception prevents the rule from generating an alert. Likewise, `OR` + logic is used for evaluating exceptions when more than one exception + list is assigned to a rule. To use the `AND` operator, you can define + multiple clauses (`entries`) in a single exception item. operationId: CreateSharedExceptionList requestBody: content: @@ -1325,7 +1371,7 @@ paths: schema: $ref: '#/components/schemas/SiemErrorResponse' description: Internal server error response - summary: Creates a shared exception list + summary: Create a shared exception list tags: - Security Solution Exceptions API components: diff --git a/x-pack/plugins/alerting/server/usage/alerting_usage_collector.ts b/x-pack/plugins/alerting/server/usage/alerting_usage_collector.ts index 5efd2d7a49152..46cf58d838ecc 100644 --- a/x-pack/plugins/alerting/server/usage/alerting_usage_collector.ts +++ b/x-pack/plugins/alerting/server/usage/alerting_usage_collector.ts @@ -205,6 +205,9 @@ export function createAlertingUsageCollector( count_rules_with_tags: 0, count_rules_snoozed: 0, count_rules_muted: 0, + count_mw_total: 0, + count_mw_with_repeat_toggle_on: 0, + count_mw_with_filter_alert_toggle_on: 0, count_rules_with_muted_alerts: 0, count_connector_types_by_consumers: {}, count_rules_by_execution_status_per_day: {}, @@ -289,6 +292,9 @@ export function createAlertingUsageCollector( count_rules_by_notify_when: byNotifyWhenSchema, count_rules_snoozed: { type: 'long' }, count_rules_muted: { type: 'long' }, + count_mw_total: { type: 'long' }, + count_mw_with_repeat_toggle_on: { type: 'long' }, + count_mw_with_filter_alert_toggle_on: { type: 'long' }, count_rules_with_muted_alerts: { type: 'long' }, count_connector_types_by_consumers: { DYNAMIC_KEY: { DYNAMIC_KEY: { type: 'long' } } }, count_rules_by_execution_status_per_day: byStatusPerDaySchema, diff --git a/x-pack/plugins/alerting/server/usage/lib/get_telemetry_from_kibana.test.ts b/x-pack/plugins/alerting/server/usage/lib/get_telemetry_from_kibana.test.ts index f29602458fd50..7c06e9867dae3 100644 --- a/x-pack/plugins/alerting/server/usage/lib/get_telemetry_from_kibana.test.ts +++ b/x-pack/plugins/alerting/server/usage/lib/get_telemetry_from_kibana.test.ts @@ -6,11 +6,97 @@ */ import { elasticsearchServiceMock, loggingSystemMock } from '@kbn/core/server/mocks'; -import { getTotalCountAggregations, getTotalCountInUse } from './get_telemetry_from_kibana'; +import { + getTotalCountAggregations, + getTotalCountInUse, + getMWTelemetry, +} from './get_telemetry_from_kibana'; +import { savedObjectsClientMock } from '@kbn/core/server/mocks'; +import { MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE } from '../../../common'; +import { ISavedObjectsRepository } from '@kbn/core/server'; const elasticsearch = elasticsearchServiceMock.createStart(); const esClient = elasticsearch.client.asInternalUser; const logger: ReturnType = loggingSystemMock.createLogger(); +const savedObjectsClient = savedObjectsClientMock.create() as unknown as ISavedObjectsRepository; +const thrownError = new Error('Fail'); + +const mockedResponse = { + saved_objects: [ + { + id: '1', + type: MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, + attributes: { + title: 'test_rule_1', + enabled: true, + duration: 1800000, + expirationDate: '2025-09-09T13:13:07.824Z', + events: [], + rRule: { + dtstart: '2024-09-09T13:13:02.054Z', + tzid: 'Europe/Stockholm', + freq: 0, + count: 1, + }, + createdBy: null, + updatedBy: null, + createdAt: '2024-09-09T13:13:07.825Z', + updatedAt: '2024-09-09T13:13:07.825Z', + scopedQuery: null, + }, + }, + { + id: '2', + type: MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, + attributes: { + title: 'test_rule_2', + enabled: true, + duration: 1800000, + expirationDate: '2025-09-09T13:13:07.824Z', + events: [], + rRule: { + dtstart: '2024-09-09T13:13:02.054Z', + tzid: 'Europe/Stockholm', + freq: 3, + interval: 1, + byweekday: ['SU'], + }, + createdBy: null, + updatedBy: null, + createdAt: '2024-09-09T13:13:07.825Z', + updatedAt: '2024-09-09T13:13:07.825Z', + scopedQuery: { + filters: [], + kql: 'kibana.alert.job_errors_results.job_id : * ', + dsl: '{"bool":{"must":[],"filter":[{"bool":{"should":[{"exists":{"field":"kibana.alert.job_errors_results.job_id"}}],"minimum_should_match":1}}],"should":[],"must_not":[]}}', + }, + }, + }, + { + id: '3', + type: MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, + attributes: { + title: 'test_rule_3', + enabled: true, + duration: 1800000, + expirationDate: '2025-09-09T13:13:07.824Z', + events: [], + rRule: { + dtstart: '2024-09-09T13:13:02.054Z', + tzid: 'Europe/Stockholm', + freq: 3, + interval: 1, + byweekday: ['TU'], + }, + createdBy: null, + updatedBy: null, + createdAt: '2024-09-09T13:13:07.825Z', + updatedAt: '2024-09-09T13:13:07.825Z', + scopedQuery: null, + }, + }, + ], +}; describe('kibana index telemetry', () => { beforeEach(() => { @@ -420,4 +506,94 @@ describe('kibana index telemetry', () => { }); }); }); + + describe('getMWTelemetry', () => { + test('should return MW telemetry', async () => { + savedObjectsClient.createPointInTimeFinder = jest.fn().mockReturnValue({ + close: jest.fn(), + find: jest.fn().mockImplementation(async function* () { + yield mockedResponse; + }), + }); + const telemetry = await getMWTelemetry({ + savedObjectsClient, + logger, + }); + + expect(savedObjectsClient.createPointInTimeFinder).toHaveBeenCalledWith({ + type: MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, + namespaces: ['*'], + perPage: 100, + fields: ['rRule', 'scopedQuery'], + }); + expect(telemetry).toStrictEqual({ + count_mw_total: 3, + count_mw_with_repeat_toggle_on: 2, + count_mw_with_filter_alert_toggle_on: 1, + hasErrors: false, + }); + }); + }); + + test('should throw the error', async () => { + savedObjectsClient.createPointInTimeFinder = jest.fn().mockReturnValue({ + close: jest.fn(), + find: jest.fn().mockImplementation(async function* () { + throw thrownError; + }), + }); + + const telemetry = await getMWTelemetry({ + savedObjectsClient, + logger, + }); + + expect(savedObjectsClient.createPointInTimeFinder).toHaveBeenCalledWith({ + type: MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, + namespaces: ['*'], + perPage: 100, + fields: ['rRule', 'scopedQuery'], + }); + + expect(telemetry).toStrictEqual({ + count_mw_total: 0, + count_mw_with_repeat_toggle_on: 0, + count_mw_with_filter_alert_toggle_on: 0, + hasErrors: true, + errorMessage: 'Fail', + }); + expect(logger.warn).toHaveBeenCalled(); + const loggerCall = logger.warn.mock.calls[0][0]; + const loggerMeta = logger.warn.mock.calls[0][1]; + expect(loggerCall).toBe('Error executing alerting telemetry task: getTotalMWCount - {}'); + expect(loggerMeta?.tags).toEqual(['alerting', 'telemetry-failed']); + expect(loggerMeta?.error?.stack_trace).toBeDefined(); + }); + + test('should stop on MW max limit count', async () => { + savedObjectsClient.createPointInTimeFinder = jest.fn().mockReturnValue({ + close: jest.fn(), + find: jest.fn().mockImplementation(async function* () { + yield mockedResponse; + }), + }); + const telemetry = await getMWTelemetry({ + savedObjectsClient, + logger, + maxDocuments: 1, + }); + + expect(savedObjectsClient.createPointInTimeFinder).toHaveBeenCalledWith({ + type: MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, + namespaces: ['*'], + perPage: 100, + fields: ['rRule', 'scopedQuery'], + }); + expect(telemetry).toStrictEqual({ + count_mw_total: 2, + count_mw_with_repeat_toggle_on: 1, + count_mw_with_filter_alert_toggle_on: 1, + hasErrors: false, + }); + }); }); diff --git a/x-pack/plugins/alerting/server/usage/lib/get_telemetry_from_kibana.ts b/x-pack/plugins/alerting/server/usage/lib/get_telemetry_from_kibana.ts index fdfdbf1dbcfe6..756512815d901 100644 --- a/x-pack/plugins/alerting/server/usage/lib/get_telemetry_from_kibana.ts +++ b/x-pack/plugins/alerting/server/usage/lib/get_telemetry_from_kibana.ts @@ -11,7 +11,7 @@ import type { AggregationsTermsAggregateBase, AggregationsStringTermsBucketKeys, } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { ElasticsearchClient, Logger, ISavedObjectsRepository } from '@kbn/core/server'; import { ConnectorsByConsumersBucket, @@ -23,6 +23,8 @@ import { AlertingUsage } from '../types'; import { NUM_ALERTING_RULE_TYPES } from '../alerting_usage_collector'; import { parseSimpleRuleTypeBucket } from './parse_simple_rule_type_bucket'; import { groupRulesBySearchType } from './group_rules_by_search_type'; +import { MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE } from '../../../common'; +import { MaintenanceWindowAttributes } from '../../data/maintenance_window/types'; interface Opts { esClient: ElasticsearchClient; @@ -30,6 +32,12 @@ interface Opts { logger: Logger; } +interface MWOpts { + savedObjectsClient: ISavedObjectsRepository; + logger: Logger; + maxDocuments?: number; +} + type GetTotalCountsResults = Pick< AlertingUsage, | 'count_total' @@ -48,6 +56,14 @@ type GetTotalCountsResults = Pick< | 'connectors_per_alert' > & { errorMessage?: string; hasErrors: boolean }; +type GetMWTelemetryResults = Pick< + AlertingUsage, + 'count_mw_total' | 'count_mw_with_repeat_toggle_on' | 'count_mw_with_filter_alert_toggle_on' +> & { + errorMessage?: string; + hasErrors: boolean; +}; + interface GetTotalCountInUseResults { countTotal: number; countByType: Record; @@ -56,6 +72,8 @@ interface GetTotalCountInUseResults { hasErrors: boolean; } +const TELEMETRY_MW_COUNT_LIMIT = 10000; + export async function getTotalCountAggregations({ esClient, alertIndex, @@ -490,3 +508,60 @@ export async function getTotalCountInUse({ }; } } + +export async function getMWTelemetry({ + savedObjectsClient, + logger, + maxDocuments = TELEMETRY_MW_COUNT_LIMIT, +}: MWOpts): Promise { + try { + const mwFinder = savedObjectsClient.createPointInTimeFinder({ + type: MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE, + namespaces: ['*'], + perPage: 100, + fields: ['rRule', 'scopedQuery'], + }); + + let countMWTotal = 0; + let countMWWithRepeatToggleON = 0; + let countMWWithFilterAlertToggleON = 0; + mwLoop: for await (const response of mwFinder.find()) { + for (const mwSavedObject of response.saved_objects) { + if (countMWTotal > maxDocuments) break mwLoop; + countMWTotal = countMWTotal + 1; + // scopedQuery property will be null if "Filter alerts" toggle will be off + if (mwSavedObject.attributes.scopedQuery) { + countMWWithFilterAlertToggleON = countMWWithFilterAlertToggleON + 1; + } + // interval property will be not in place if "Repeat" toggle will be off + if (Object.hasOwn(mwSavedObject.attributes.rRule, 'interval')) { + countMWWithRepeatToggleON = countMWWithRepeatToggleON + 1; + } + } + } + await mwFinder.close(); + + return { + hasErrors: false, + count_mw_total: countMWTotal, + count_mw_with_repeat_toggle_on: countMWWithRepeatToggleON, + count_mw_with_filter_alert_toggle_on: countMWWithFilterAlertToggleON, + }; + } catch (err) { + const errorMessage = err?.message ? err.message : err.toString(); + logger.warn( + `Error executing alerting telemetry task: getTotalMWCount - ${JSON.stringify(err)}`, + { + tags: ['alerting', 'telemetry-failed'], + error: { stack_trace: err?.stack }, + } + ); + return { + hasErrors: true, + errorMessage, + count_mw_total: 0, + count_mw_with_repeat_toggle_on: 0, + count_mw_with_filter_alert_toggle_on: 0, + }; + } +} diff --git a/x-pack/plugins/alerting/server/usage/task.ts b/x-pack/plugins/alerting/server/usage/task.ts index 0cc08db911226..41db75032ef4d 100644 --- a/x-pack/plugins/alerting/server/usage/task.ts +++ b/x-pack/plugins/alerting/server/usage/task.ts @@ -12,15 +12,19 @@ import { TaskManagerStartContract, IntervalSchedule, } from '@kbn/task-manager-plugin/server'; - import { getFailedAndUnrecognizedTasksPerDay } from './lib/get_telemetry_from_task_manager'; -import { getTotalCountAggregations, getTotalCountInUse } from './lib/get_telemetry_from_kibana'; +import { + getTotalCountAggregations, + getTotalCountInUse, + getMWTelemetry, +} from './lib/get_telemetry_from_kibana'; import { getExecutionsPerDayCount, getExecutionTimeoutsPerDayCount, } from './lib/get_telemetry_from_event_log'; import { stateSchemaByVersion, emptyState, type LatestTaskStateSchema } from './task_state'; import { RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; +import { MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE } from '../../common'; export const TELEMETRY_TASK_TYPE = 'alerting_telemetry'; @@ -36,12 +40,6 @@ export function initializeAlertingTelemetry( registerAlertingTelemetryTask(logger, core, taskManager, eventLogIndex); } -export function scheduleAlertingTelemetry(logger: Logger, taskManager?: TaskManagerStartContract) { - if (taskManager) { - scheduleTasks(logger, taskManager).catch(() => {}); // it shouldn't reject, but just in case - } -} - function registerAlertingTelemetryTask( logger: Logger, core: CoreSetup, @@ -58,6 +56,12 @@ function registerAlertingTelemetryTask( }); } +export function scheduleAlertingTelemetry(logger: Logger, taskManager?: TaskManagerStartContract) { + if (taskManager) { + scheduleTasks(logger, taskManager).catch(() => {}); // it shouldn't reject, but just in case + } +} + async function scheduleTasks(logger: Logger, taskManager: TaskManagerStartContract) { try { await taskManager.ensureScheduled({ @@ -93,16 +97,26 @@ export function telemetryTaskRunner( .getStartServices() .then(([coreStart]) => coreStart.savedObjects.getIndexForType(RULE_SAVED_OBJECT_TYPE)); + const getSavedObjectClient = () => + core + .getStartServices() + .then(([coreStart]) => + coreStart.savedObjects.createInternalRepository([MAINTENANCE_WINDOW_SAVED_OBJECT_TYPE]) + ); + return { async run() { const esClient = await getEsClient(); const alertIndex = await getAlertIndex(); + const savedObjectsClient = await getSavedObjectClient(); + return Promise.all([ getTotalCountAggregations({ esClient, alertIndex, logger }), getTotalCountInUse({ esClient, alertIndex, logger }), getExecutionsPerDayCount({ esClient, eventLogIndex, logger }), getExecutionTimeoutsPerDayCount({ esClient, eventLogIndex, logger }), getFailedAndUnrecognizedTasksPerDay({ esClient, taskManagerIndex, logger }), + getMWTelemetry({ logger, savedObjectsClient }), ]) .then( ([ @@ -111,13 +125,15 @@ export function telemetryTaskRunner( dailyExecutionCounts, dailyExecutionTimeoutCounts, dailyFailedAndUnrecognizedTasks, + MWTelemetry, ]) => { const hasErrors = totalCountAggregations.hasErrors || totalInUse.hasErrors || dailyExecutionCounts.hasErrors || dailyExecutionTimeoutCounts.hasErrors || - dailyFailedAndUnrecognizedTasks.hasErrors; + dailyFailedAndUnrecognizedTasks.hasErrors || + MWTelemetry.hasErrors; const errorMessages = [ totalCountAggregations.errorMessage, @@ -125,6 +141,7 @@ export function telemetryTaskRunner( dailyExecutionCounts.errorMessage, dailyExecutionTimeoutCounts.errorMessage, dailyFailedAndUnrecognizedTasks.errorMessage, + MWTelemetry.errorMessage, ].filter((message) => message !== undefined); const updatedState: LatestTaskStateSchema = { @@ -147,6 +164,10 @@ export function telemetryTaskRunner( count_rules_by_notify_when: totalCountAggregations.count_rules_by_notify_when, count_rules_snoozed: totalCountAggregations.count_rules_snoozed, count_rules_muted: totalCountAggregations.count_rules_muted, + count_mw_total: MWTelemetry.count_mw_total, + count_mw_with_repeat_toggle_on: MWTelemetry.count_mw_with_repeat_toggle_on, + count_mw_with_filter_alert_toggle_on: + MWTelemetry.count_mw_with_filter_alert_toggle_on, count_rules_with_muted_alerts: totalCountAggregations.count_rules_with_muted_alerts, count_connector_types_by_consumers: totalCountAggregations.count_connector_types_by_consumers, diff --git a/x-pack/plugins/alerting/server/usage/task_state.ts b/x-pack/plugins/alerting/server/usage/task_state.ts index cbcabeb490b84..a9652ee8200a1 100644 --- a/x-pack/plugins/alerting/server/usage/task_state.ts +++ b/x-pack/plugins/alerting/server/usage/task_state.ts @@ -146,6 +146,9 @@ export const stateSchemaByVersion = { }), count_rules_snoozed: schema.number(), count_rules_muted: schema.number(), + count_mw_total: schema.number(), + count_mw_with_repeat_toggle_on: schema.number(), + count_mw_with_filter_alert_toggle_on: schema.number(), count_rules_with_muted_alerts: schema.number(), count_connector_types_by_consumers: schema.recordOf( schema.string(), @@ -248,6 +251,9 @@ export const emptyState: LatestTaskStateSchema = { }, count_rules_snoozed: 0, count_rules_muted: 0, + count_mw_total: 0, + count_mw_with_repeat_toggle_on: 0, + count_mw_with_filter_alert_toggle_on: 0, count_rules_with_muted_alerts: 0, count_connector_types_by_consumers: {}, count_rules_namespaces: 0, diff --git a/x-pack/plugins/alerting/server/usage/types.ts b/x-pack/plugins/alerting/server/usage/types.ts index 15c0f0a962710..ece69ace7ba5d 100644 --- a/x-pack/plugins/alerting/server/usage/types.ts +++ b/x-pack/plugins/alerting/server/usage/types.ts @@ -41,6 +41,9 @@ export interface AlertingUsage { count_connector_types_by_consumers: Record>; count_rules_snoozed: number; count_rules_muted: number; + count_mw_total: number; + count_mw_with_repeat_toggle_on: number; + count_mw_with_filter_alert_toggle_on: number; count_rules_with_muted_alerts: number; count_rules_by_execution_status_per_day: Record; percentile_num_generated_actions_per_day: { diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/pipeline_processors_editor.test.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/pipeline_processors_editor.test.tsx index dcb234b548903..90bba1b4ed14a 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/pipeline_processors_editor.test.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/pipeline_processors_editor.test.tsx @@ -395,4 +395,39 @@ describe('Pipeline Editor', () => { assertTestProcessor({ description: processorDescriptions.none, descriptionVisible: false }); }); }); + + describe('object values', () => { + const mockData: Pick = { + processors: [ + { + set: { + field: 'test', + value: { test: 'test' }, + }, + }, + { + append: { + field: 'test', + value: { test: 'test' }, + }, + }, + ], + }; + it('editor works when value is an object', async () => { + onUpdate = jest.fn(); + testBed = await setup({ + value: { + ...mockData, + }, + onFlyoutOpen: jest.fn(), + onUpdate, + }); + expect(testBed.find(`processors>0.inlineTextInputNonEditableText`).text()).toBe( + 'Sets value of "test" to "{"test":"test"}"' + ); + expect(testBed.find(`processors>1.inlineTextInputNonEditableText`).text()).toBe( + 'Appends "{"test":"test"}" to the "test" field' + ); + }); + }); }); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx index a4a838f92d1ab..5d672deb739d3 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/map_processor_type_to_form.tsx @@ -10,6 +10,7 @@ import React, { ReactNode } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiCode, EuiLink } from '@elastic/eui'; +import { stringifyValueDescription } from './stringify_value_description'; import { LicenseType } from '../../../../../types'; import { @@ -127,7 +128,7 @@ export const mapProcessorTypeToDescriptor: MapProcessorTypeToDescriptor = { defaultMessage: 'Appends "{value}" to the "{field}" field', values: { field, - value, + value: stringifyValueDescription(value), }, }), }, @@ -810,7 +811,7 @@ export const mapProcessorTypeToDescriptor: MapProcessorTypeToDescriptor = { defaultMessage: 'Sets value of "{field}" to "{value}"', values: { field, - value, + value: stringifyValueDescription(value), }, }); }, diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/stringify_value_description.test.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/stringify_value_description.test.ts new file mode 100644 index 0000000000000..62389782a601d --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/stringify_value_description.test.ts @@ -0,0 +1,29 @@ +/* + * 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 { stringifyValueDescription } from './stringify_value_description'; + +describe('StringifyValueDescription', () => { + it('works for a json object', () => { + expect(stringifyValueDescription({ test: 'test' })).toEqual('{"test":"test"}'); + }); + it('works for an array', () => { + expect(stringifyValueDescription(['a', 'b'])).toEqual('["a","b"]'); + }); + it('works for a string', () => { + expect(stringifyValueDescription('test')).toEqual('test'); + }); + it('works for a number', () => { + expect(stringifyValueDescription(123)).toEqual('123'); + }); + it('empty string for undefined', () => { + expect(stringifyValueDescription(undefined)).toEqual(''); + }); + it('empty string for null', () => { + expect(stringifyValueDescription(null)).toEqual(''); + }); +}); diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/stringify_value_description.ts b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/stringify_value_description.ts new file mode 100644 index 0000000000000..d2ad726aed64e --- /dev/null +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/components/shared/stringify_value_description.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const stringifyValueDescription = (value: unknown): string => { + if (!value) { + return ''; + } + if (typeof value === 'object') { + try { + // if the value is a json object, it will be stringified as json + return JSON.stringify(value); + } catch (e) { + // ignore any errors + } + } + // otherwise just return a stringified value + return String(value); +}; diff --git a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json index 11c1a0a7edee0..ff79fcf4632a3 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -1724,6 +1724,15 @@ "count_rules_muted": { "type": "long" }, + "count_mw_total": { + "type": "long" + }, + "count_mw_with_repeat_toggle_on": { + "type": "long" + }, + "count_mw_with_filter_alert_toggle_on": { + "type": "long" + }, "count_rules_with_muted_alerts": { "type": "long" }, diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/telemetry/alerting_and_actions_telemetry.ts b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/telemetry/alerting_and_actions_telemetry.ts index 000ff81d2d2dc..447a49bba4938 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/telemetry/alerting_and_actions_telemetry.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/telemetry/alerting_and_actions_telemetry.ts @@ -90,6 +90,44 @@ export default function createAlertingAndActionsTelemetryTests({ getService }: F return ruleResponse.body.id; } + async function createMaintenanceWindow({ + spaceId, + interval, + scopedQuery = null, + }: { + spaceId: string; + interval?: number; + scopedQuery?: { + filters: string[]; + kql: string; + dsl: string; + } | null; + }) { + const response = await supertestWithoutAuth + .post(`${getUrlPrefix(spaceId)}/internal/alerting/rules/maintenance_window`) + .set('kbn-xsrf', 'foo') + .auth(Superuser.username, Superuser.password) + .send({ + title: 'test-maintenance-window', + duration: 60 * 60 * 1000, // 1 hr + r_rule: { + dtstart: new Date().toISOString(), + tzid: 'UTC', + freq: 0, + count: 1, + ...(interval ? { interval } : {}), + }, + category_ids: ['management'], + scoped_query: scopedQuery, + }); + + expect(response.status).to.equal(200); + + objectRemover.add(spaceId, response.body.id, 'rules/maintenance_window', 'alerting', true); + + return response.body.id; + } + async function setup() { // Create rules and connectors in multiple spaces for (const space of Spaces) { @@ -216,6 +254,18 @@ export default function createAlertingAndActionsTelemetryTests({ getService }: F actions: [], }, }); + // MW with both toggles off + await createMaintenanceWindow({ spaceId: space.id }); + // MW with 'Repeat' toggle on and 'Filter alerts' toggle on + await createMaintenanceWindow({ + spaceId: space.id, + interval: 1, + scopedQuery: { + filters: [], + kql: 'kibana.alert.job_errors_results.job_id : * ', + dsl: '{"bool":{"must":[],"filter":[{"bool":{"should":[{"exists":{"field":"kibana.alert.job_errors_results.job_id"}}],"minimum_should_match":1}}],"should":[],"must_not":[]}}', + }, + }); } } @@ -500,6 +550,11 @@ export default function createAlertingAndActionsTelemetryTests({ getService }: F expect(telemetry.count_rules_by_execution_status_per_day.failure > 0).to.be(true); expect(telemetry.count_rules_by_execution_status_per_day.success > 0).to.be(true); + + // maintenance window telemetry + expect(telemetry.count_mw_total).to.equal(6); + expect(telemetry.count_mw_with_filter_alert_toggle_on).to.equal(3); + expect(telemetry.count_mw_with_repeat_toggle_on).to.equal(3); } it('should retrieve telemetry data in the expected format', async () => { @@ -527,7 +582,7 @@ export default function createAlertingAndActionsTelemetryTests({ getService }: F let actionsTelemetry: any; await retry.try(async () => { const telemetryTask = await es.get({ - id: `task:Actions-actions_telemetry`, + id: 'task:Actions-actions_telemetry', index: '.kibana_task_manager', }); expect(telemetryTask!._source!.task?.status).to.be('idle'); @@ -550,7 +605,7 @@ export default function createAlertingAndActionsTelemetryTests({ getService }: F let alertingTelemetry: any; await retry.try(async () => { const telemetryTask = await es.get({ - id: `task:Alerting-alerting_telemetry`, + id: 'task:Alerting-alerting_telemetry', index: '.kibana_task_manager', }); expect(telemetryTask!._source!.task?.status).to.be('idle'); diff --git a/x-pack/test/api_integration/services/security_solution_exceptions_api.gen.ts b/x-pack/test/api_integration/services/security_solution_exceptions_api.gen.ts index e7426a737b39c..726f877898918 100644 --- a/x-pack/test/api_integration/services/security_solution_exceptions_api.gen.ts +++ b/x-pack/test/api_integration/services/security_solution_exceptions_api.gen.ts @@ -45,6 +45,12 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) const supertest = getService('supertest'); return { + /** + * An exception list groups exception items and can be associated with detection rules. You can assign detection rules with multiple exception lists. +> info +> All exception items added to the same list are evaluated using `OR` logic. That is, if any of the items in a list evaluate to `true`, the exception prevents the rule from generating an alert. Likewise, `OR` logic is used for evaluating exceptions when more than one exception list is assigned to a rule. To use the `AND` operator, you can define multiple clauses (`entries`) in a single exception item. + + */ createExceptionList(props: CreateExceptionListProps) { return supertest .post('/api/exception_lists') @@ -53,6 +59,12 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .send(props.body as object); }, + /** + * Create an exception item and associate it with the specified exception list. +> info +> Before creating exception items, you must create an exception list. + + */ createExceptionListItem(props: CreateExceptionListItemProps) { return supertest .post('/api/exception_lists/items') @@ -61,6 +73,9 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .send(props.body as object); }, + /** + * Create exception items that apply to a single detection rule. + */ createRuleExceptionListItems(props: CreateRuleExceptionListItemsProps) { return supertest .post(replaceParams('/api/detection_engine/rules/{id}/exceptions', props.params)) @@ -69,6 +84,12 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .send(props.body as object); }, + /** + * An exception list groups exception items and can be associated with detection rules. A shared exception list can apply to multiple detection rules. +> info +> All exception items added to the same list are evaluated using `OR` logic. That is, if any of the items in a list evaluate to `true`, the exception prevents the rule from generating an alert. Likewise, `OR` logic is used for evaluating exceptions when more than one exception list is assigned to a rule. To use the `AND` operator, you can define multiple clauses (`entries`) in a single exception item. + + */ createSharedExceptionList(props: CreateSharedExceptionListProps) { return supertest .post('/api/exceptions/shared') @@ -77,6 +98,9 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .send(props.body as object); }, + /** + * Delete an exception list using the `id` or `list_id` field. + */ deleteExceptionList(props: DeleteExceptionListProps) { return supertest .delete('/api/exception_lists') @@ -85,6 +109,9 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .query(props.query); }, + /** + * Delete an exception list item using the `id` or `item_id` field. + */ deleteExceptionListItem(props: DeleteExceptionListItemProps) { return supertest .delete('/api/exception_lists/items') @@ -93,6 +120,9 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .query(props.query); }, + /** + * Duplicate an existing exception list. + */ duplicateExceptionList(props: DuplicateExceptionListProps) { return supertest .post('/api/exception_lists/_duplicate') @@ -102,7 +132,7 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) .query(props.query); }, /** - * Exports an exception list and its associated items to an .ndjson file + * Export an exception list and its associated items to an NDJSON file. */ exportExceptionList(props: ExportExceptionListProps) { return supertest @@ -112,6 +142,9 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .query(props.query); }, + /** + * Get a list of all exception list items in the specified list. + */ findExceptionListItems(props: FindExceptionListItemsProps) { return supertest .get('/api/exception_lists/items/_find') @@ -120,6 +153,9 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .query(props.query); }, + /** + * Get a list of all exception lists. + */ findExceptionLists(props: FindExceptionListsProps) { return supertest .get('/api/exception_lists/_find') @@ -129,7 +165,7 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) .query(props.query); }, /** - * Imports an exception list and associated items + * Import an exception list and its associated items from an NDJSON file. */ importExceptionList(props: ImportExceptionListProps) { return supertest @@ -139,6 +175,9 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .query(props.query); }, + /** + * Get the details of an exception list using the `id` or `list_id` field. + */ readExceptionList(props: ReadExceptionListProps) { return supertest .get('/api/exception_lists') @@ -147,6 +186,9 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .query(props.query); }, + /** + * Get the details of an exception list item using the `id` or `item_id` field. + */ readExceptionListItem(props: ReadExceptionListItemProps) { return supertest .get('/api/exception_lists/items') @@ -155,6 +197,9 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .query(props.query); }, + /** + * Get a summary of the specified exception list. + */ readExceptionListSummary(props: ReadExceptionListSummaryProps) { return supertest .get('/api/exception_lists/summary') @@ -163,6 +208,9 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .query(props.query); }, + /** + * Update an exception list using the `id` or `list_id` field. + */ updateExceptionList(props: UpdateExceptionListProps) { return supertest .put('/api/exception_lists') @@ -171,6 +219,9 @@ export function SecuritySolutionApiProvider({ getService }: FtrProviderContext) .set(X_ELASTIC_INTERNAL_ORIGIN_REQUEST, 'kibana') .send(props.body as object); }, + /** + * Update an exception list item using the `id` or `item_id` field. + */ updateExceptionListItem(props: UpdateExceptionListItemProps) { return supertest .put('/api/exception_lists/items')