diff --git a/packages/kbn-rule-data-utils/src/technical_field_names.ts b/packages/kbn-rule-data-utils/src/technical_field_names.ts index c6edd30549a76..e49b47c712780 100644 --- a/packages/kbn-rule-data-utils/src/technical_field_names.ts +++ b/packages/kbn-rule-data-utils/src/technical_field_names.ts @@ -47,6 +47,7 @@ const ALERT_RULE_CREATED_AT = `${ALERT_RULE_NAMESPACE}.created_at` as const; const ALERT_RULE_CREATED_BY = `${ALERT_RULE_NAMESPACE}.created_by` as const; const ALERT_RULE_DESCRIPTION = `${ALERT_RULE_NAMESPACE}.description` as const; const ALERT_RULE_ENABLED = `${ALERT_RULE_NAMESPACE}.enabled` as const; +const ALERT_RULE_EXECUTION_UUID = `${ALERT_RULE_NAMESPACE}.execution.uuid` as const; const ALERT_RULE_FROM = `${ALERT_RULE_NAMESPACE}.from` as const; const ALERT_RULE_INTERVAL = `${ALERT_RULE_NAMESPACE}.interval` as const; const ALERT_RULE_LICENSE = `${ALERT_RULE_NAMESPACE}.license` as const; @@ -103,6 +104,7 @@ const fields = { ALERT_RULE_CREATED_BY, ALERT_RULE_DESCRIPTION, ALERT_RULE_ENABLED, + ALERT_RULE_EXECUTION_UUID, ALERT_RULE_FROM, ALERT_RULE_INTERVAL, ALERT_RULE_LICENSE, @@ -156,6 +158,7 @@ export { ALERT_RULE_CREATED_BY, ALERT_RULE_DESCRIPTION, ALERT_RULE_ENABLED, + ALERT_RULE_EXECUTION_UUID, ALERT_RULE_FROM, ALERT_RULE_INTERVAL, ALERT_RULE_LICENSE, diff --git a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts index a7a00034e7064..3895c90d4a6c2 100644 --- a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts +++ b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.test.ts @@ -25,6 +25,7 @@ describe('createAlertEventLogRecordObject', () => { test('created alert event "execute-start"', async () => { expect( createAlertEventLogRecordObject({ + executionId: '7a7065d7-6e8b-4aae-8d20-c93613dec9fb', ruleId: '1', ruleType, action: 'execute-start', @@ -50,6 +51,13 @@ describe('createAlertEventLogRecordObject', () => { kind: 'alert', }, kibana: { + alert: { + rule: { + execution: { + uuid: '7a7065d7-6e8b-4aae-8d20-c93613dec9fb', + }, + }, + }, saved_objects: [ { id: '1', @@ -76,6 +84,7 @@ describe('createAlertEventLogRecordObject', () => { test('created alert event "recovered-instance"', async () => { expect( createAlertEventLogRecordObject({ + executionId: '7a7065d7-6e8b-4aae-8d20-c93613dec9fb', ruleId: '1', ruleName: 'test name', ruleType, @@ -109,6 +118,13 @@ describe('createAlertEventLogRecordObject', () => { start: '1970-01-01T00:00:00.000Z', }, kibana: { + alert: { + rule: { + execution: { + uuid: '7a7065d7-6e8b-4aae-8d20-c93613dec9fb', + }, + }, + }, alerting: { action_group_id: 'group 1', action_subgroup: 'subgroup value', @@ -138,6 +154,7 @@ describe('createAlertEventLogRecordObject', () => { test('created alert event "execute-action"', async () => { expect( createAlertEventLogRecordObject({ + executionId: '7a7065d7-6e8b-4aae-8d20-c93613dec9fb', ruleId: '1', ruleName: 'test name', ruleType, @@ -176,6 +193,13 @@ describe('createAlertEventLogRecordObject', () => { start: '1970-01-01T00:00:00.000Z', }, kibana: { + alert: { + rule: { + execution: { + uuid: '7a7065d7-6e8b-4aae-8d20-c93613dec9fb', + }, + }, + }, alerting: { action_group_id: 'group 1', action_subgroup: 'subgroup value', diff --git a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts index e06b5bf893bac..95e33d394fbd2 100644 --- a/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts +++ b/x-pack/plugins/alerting/server/lib/create_alert_event_log_record_object.ts @@ -12,6 +12,7 @@ import { UntypedNormalizedRuleType } from '../rule_type_registry'; export type Event = Exclude; interface CreateAlertEventLogRecordParams { + executionId?: string; ruleId: string; ruleType: UntypedNormalizedRuleType; action: string; @@ -36,7 +37,18 @@ interface CreateAlertEventLogRecordParams { } export function createAlertEventLogRecordObject(params: CreateAlertEventLogRecordParams): Event { - const { ruleType, action, state, message, task, ruleId, group, subgroup, namespace } = params; + const { + executionId, + ruleType, + action, + state, + message, + task, + ruleId, + group, + subgroup, + namespace, + } = params; const alerting = params.instanceId || group || subgroup ? { @@ -59,6 +71,17 @@ export function createAlertEventLogRecordObject(params: CreateAlertEventLogRecor }, kibana: { ...(alerting ? alerting : {}), + ...(executionId + ? { + alert: { + rule: { + execution: { + uuid: executionId, + }, + }, + }, + } + : {}), saved_objects: params.savedObjects.map((so) => ({ ...(so.relation ? { rel: so.relation } : {}), type: so.type, diff --git a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts index a5b9f1d928e81..9c3e5872c76e1 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts @@ -21,6 +21,10 @@ import { getBeforeSetup, setGlobalDate } from './lib'; import { eventLoggerMock } from '../../../../event_log/server/event_logger.mock'; import { TaskStatus } from '../../../../task_manager/server'; +jest.mock('uuid', () => ({ + v4: () => '5f6aa57d-3e22-484e-bae8-cbed868f4d28', +})); + const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); diff --git a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts index 69b094585d703..71ec12e29a9dd 100644 --- a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.test.ts @@ -73,6 +73,7 @@ const createExecutionHandlerParams: jest.Mocked< spaceId: 'test1', ruleId: '1', ruleName: 'name-of-alert', + executionId: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', tags: ['tag-A', 'tag-B'], apiKey: 'MTIzOmFiYw==', kibanaBaseUrl: 'http://localhost:5601', @@ -173,6 +174,13 @@ test('enqueues execution per selected action', async () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "action_group_id": "default", "instance_id": "2", diff --git a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts index 112cb949e3ad7..58f8089890c87 100644 --- a/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts +++ b/x-pack/plugins/alerting/server/task_runner/create_execution_handler.ts @@ -36,6 +36,7 @@ export interface CreateExecutionHandlerOptions< > { ruleId: string; ruleName: string; + executionId: string; tags?: string[]; actionsPlugin: ActionsPluginStartContract; actions: AlertAction[]; @@ -83,6 +84,7 @@ export function createExecutionHandler< logger, ruleId, ruleName, + executionId, tags, actionsPlugin, actions: ruleActions, @@ -206,6 +208,7 @@ export function createExecutionHandler< ruleId, ruleType: ruleType as UntypedNormalizedRuleType, action: EVENT_LOG_ACTIONS.executeAction, + executionId, instanceId: alertId, group: actionGroup, subgroup: actionSubgroup, diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts index 61d41e674c209..a466583cd3bd3 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.test.ts @@ -41,6 +41,10 @@ import { UntypedNormalizedRuleType } from '../rule_type_registry'; import { ruleTypeRegistryMock } from '../rule_type_registry.mock'; import { ExecuteOptions } from '../../../actions/server/create_execute_function'; +jest.mock('uuid', () => ({ + v4: () => '5f6aa57d-3e22-484e-bae8-cbed868f4d28', +})); + const ruleType: jest.Mocked = { id: 'test', name: 'My test rule', @@ -323,6 +327,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "saved_objects": Array [ Object { "id": "1", @@ -484,6 +495,13 @@ describe('Task Runner', () => { kind: 'alert', }, kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, task: { schedule_delay: 0, scheduled: '1970-01-01T00:00:00.000Z', @@ -515,6 +533,13 @@ describe('Task Runner', () => { start: '1970-01-01T00:00:00.000Z', }, kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, alerting: { action_group_id: 'default', action_subgroup: 'subDefault', @@ -549,6 +574,13 @@ describe('Task Runner', () => { start: '1970-01-01T00:00:00.000Z', }, kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, alerting: { action_group_id: 'default', action_subgroup: 'subDefault', @@ -576,6 +608,13 @@ describe('Task Runner', () => { kind: 'alert', }, kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, alerting: { instance_id: '1', action_group_id: 'default', @@ -611,6 +650,13 @@ describe('Task Runner', () => { expect(eventLogger.logEvent).toHaveBeenNthCalledWith(5, { event: { action: 'execute', category: ['alerts'], kind: 'alert', outcome: 'success' }, kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, alerting: { status: 'active', }, @@ -707,6 +753,13 @@ describe('Task Runner', () => { schedule_delay: 0, scheduled: '1970-01-01T00:00:00.000Z', }, + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, saved_objects: [ { id: '1', @@ -734,6 +787,13 @@ describe('Task Runner', () => { start: '1970-01-01T00:00:00.000Z', }, kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, alerting: { action_group_id: 'default', instance_id: '1', @@ -767,6 +827,13 @@ describe('Task Runner', () => { start: '1970-01-01T00:00:00.000Z', }, kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, alerting: { instance_id: '1', action_group_id: 'default', @@ -799,6 +866,13 @@ describe('Task Runner', () => { outcome: 'success', }, kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, alerting: { status: 'active', }, @@ -963,6 +1037,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "saved_objects": Array [ Object { "id": "1", @@ -998,6 +1079,13 @@ describe('Task Runner', () => { "start": "1969-12-31T00:00:00.000Z", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "action_group_id": "default", "instance_id": "1", @@ -1033,6 +1121,13 @@ describe('Task Runner', () => { "outcome": "success", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "status": "active", }, @@ -1302,6 +1397,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "saved_objects": Array [ Object { "id": "1", @@ -1337,6 +1439,13 @@ describe('Task Runner', () => { "start": "1970-01-01T00:00:00.000Z", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "action_group_id": "default", "instance_id": "1", @@ -1373,6 +1482,13 @@ describe('Task Runner', () => { "start": "1970-01-01T00:00:00.000Z", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "action_group_id": "default", "instance_id": "1", @@ -1407,6 +1523,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "action_group_id": "default", "instance_id": "1", @@ -1448,6 +1571,13 @@ describe('Task Runner', () => { "outcome": "success", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "status": "active", }, @@ -1597,6 +1727,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "saved_objects": Array [ Object { "id": "1", @@ -1633,6 +1770,13 @@ describe('Task Runner', () => { "start": "1969-12-31T06:00:00.000Z", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "instance_id": "2", }, @@ -1668,6 +1812,13 @@ describe('Task Runner', () => { "start": "1969-12-31T00:00:00.000Z", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "action_group_id": "default", "instance_id": "1", @@ -1702,6 +1853,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "action_group_id": "recovered", "instance_id": "2", @@ -1742,6 +1900,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "action_group_id": "default", "instance_id": "1", @@ -1783,6 +1948,13 @@ describe('Task Runner', () => { "outcome": "success", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "status": "active", }, @@ -2165,6 +2337,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "saved_objects": Array [ Object { "id": "1", @@ -2201,6 +2380,13 @@ describe('Task Runner', () => { "start": "1969-12-31T06:00:00.000Z", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "action_group_id": "default", "instance_id": "2", @@ -2237,6 +2423,13 @@ describe('Task Runner', () => { "start": "1969-12-31T00:00:00.000Z", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "action_group_id": "default", "instance_id": "1", @@ -2272,6 +2465,13 @@ describe('Task Runner', () => { "outcome": "success", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "status": "active", }, @@ -2546,6 +2746,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "saved_objects": Array [ Object { "id": "1", @@ -2584,6 +2791,13 @@ describe('Task Runner', () => { "reason": "execute", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "status": "error", }, @@ -2666,6 +2880,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "saved_objects": Array [ Object { "id": "1", @@ -2704,6 +2925,13 @@ describe('Task Runner', () => { "reason": "decrypt", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "status": "error", }, @@ -2795,6 +3023,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "saved_objects": Array [ Object { "id": "1", @@ -2833,6 +3068,13 @@ describe('Task Runner', () => { "reason": "license", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "status": "error", }, @@ -2924,6 +3166,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "saved_objects": Array [ Object { "id": "1", @@ -2962,6 +3211,13 @@ describe('Task Runner', () => { "reason": "unknown", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "status": "error", }, @@ -3052,6 +3308,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "saved_objects": Array [ Object { "id": "1", @@ -3090,6 +3353,13 @@ describe('Task Runner', () => { "reason": "read", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "status": "error", }, @@ -3358,6 +3628,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "saved_objects": Array [ Object { "id": "1", @@ -3393,6 +3670,13 @@ describe('Task Runner', () => { "start": "1970-01-01T00:00:00.000Z", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "action_group_id": "default", "instance_id": "1", @@ -3429,6 +3713,13 @@ describe('Task Runner', () => { "start": "1970-01-01T00:00:00.000Z", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "action_group_id": "default", "instance_id": "2", @@ -3465,6 +3756,13 @@ describe('Task Runner', () => { "start": "1970-01-01T00:00:00.000Z", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "action_group_id": "default", "instance_id": "1", @@ -3501,6 +3799,13 @@ describe('Task Runner', () => { "start": "1970-01-01T00:00:00.000Z", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "action_group_id": "default", "instance_id": "2", @@ -3536,6 +3841,13 @@ describe('Task Runner', () => { "outcome": "success", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "status": "active", }, @@ -3643,6 +3955,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "saved_objects": Array [ Object { "id": "1", @@ -3678,6 +3997,13 @@ describe('Task Runner', () => { "start": "1969-12-31T00:00:00.000Z", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "action_group_id": "default", "instance_id": "1", @@ -3714,6 +4040,13 @@ describe('Task Runner', () => { "start": "1969-12-31T06:00:00.000Z", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "action_group_id": "default", "instance_id": "2", @@ -3749,6 +4082,13 @@ describe('Task Runner', () => { "outcome": "success", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "status": "active", }, @@ -3848,6 +4188,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "saved_objects": Array [ Object { "id": "1", @@ -3881,6 +4228,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "action_group_id": "default", "instance_id": "1", @@ -3915,6 +4269,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "action_group_id": "default", "instance_id": "2", @@ -3950,6 +4311,13 @@ describe('Task Runner', () => { "outcome": "success", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "status": "active", }, @@ -4044,6 +4412,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "saved_objects": Array [ Object { "id": "1", @@ -4080,6 +4455,13 @@ describe('Task Runner', () => { "start": "1969-12-31T00:00:00.000Z", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "instance_id": "1", }, @@ -4116,6 +4498,13 @@ describe('Task Runner', () => { "start": "1969-12-31T06:00:00.000Z", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "instance_id": "2", }, @@ -4150,6 +4539,13 @@ describe('Task Runner', () => { "outcome": "success", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "status": "ok", }, @@ -4246,6 +4642,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "saved_objects": Array [ Object { "id": "1", @@ -4279,6 +4682,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "instance_id": "1", }, @@ -4312,6 +4722,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "instance_id": "2", }, @@ -4346,6 +4763,13 @@ describe('Task Runner', () => { "outcome": "success", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "alerting": Object { "status": "ok", }, @@ -4506,6 +4930,13 @@ describe('Task Runner', () => { "kind": "alert", }, "kibana": Object { + "alert": Object { + "rule": Object { + "execution": Object { + "uuid": "5f6aa57d-3e22-484e-bae8-cbed868f4d28", + }, + }, + }, "saved_objects": Array [ Object { "id": "1", @@ -4596,6 +5027,13 @@ describe('Task Runner', () => { category: ['alerts'], }, kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, saved_objects: [ { rel: 'primary', type: 'alert', id: '1', namespace: undefined, type_id: 'test' }, ], @@ -4618,6 +5056,13 @@ describe('Task Runner', () => { outcome: 'failure', }, kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, saved_objects: [ { rel: 'primary', type: 'alert', id: '1', namespace: undefined, type_id: 'test' }, ], diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner.ts b/x-pack/plugins/alerting/server/task_runner/task_runner.ts index b4fa5a1927fee..9640dd9038ce7 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -8,6 +8,7 @@ import apm from 'elastic-apm-node'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { Dictionary, pickBy, mapValues, without, cloneDeep } from 'lodash'; import type { Request } from '@hapi/hapi'; +import uuid from 'uuid'; import { addSpaceIdToPath } from '../../../spaces/server'; import { Logger, KibanaRequest } from '../../../../../src/core/server'; import { TaskRunnerContext } from './task_runner_factory'; @@ -106,6 +107,7 @@ export class TaskRunner< ActionGroupIds, RecoveryActionGroupId >; + private readonly executionId: string; private readonly ruleTypeRegistry: RuleTypeRegistry; private searchAbortController: AbortController; private cancelled: boolean; @@ -131,6 +133,7 @@ export class TaskRunner< this.ruleTypeRegistry = context.ruleTypeRegistry; this.searchAbortController = new AbortController(); this.cancelled = false; + this.executionId = uuid.v4(); } async getDecryptedAttributes( @@ -209,6 +212,7 @@ export class TaskRunner< ruleId, ruleName, tags, + executionId: this.executionId, logger: this.logger, actionsPlugin: this.context.actionsPlugin, apiKey, @@ -324,6 +328,7 @@ export class TaskRunner< updatedRuleTypeState = await this.context.executionContext.withContext(ctx, () => this.ruleType.executor({ alertId: ruleId, + executionId: this.executionId, services: { ...services, alertInstanceFactory: createAlertInstanceFactory< @@ -411,6 +416,7 @@ export class TaskRunner< if (this.shouldLogAndScheduleActionsForAlerts()) { generateNewAndRecoveredAlertEvents({ eventLogger, + executionId: this.executionId, originalAlerts, currentAlerts: alertsWithScheduledActions, recoveredAlerts, @@ -612,6 +618,7 @@ export class TaskRunner< ruleType: this.ruleType as UntypedNormalizedRuleType, action: EVENT_LOG_ACTIONS.execute, namespace, + executionId: this.executionId, task: { scheduled: this.taskInstance.runAt.toISOString(), scheduleDelay: Millis2Nanos * scheduleDelay, @@ -785,6 +792,13 @@ export class TaskRunner< this.ruleType.ruleTaskTimeout }`, kibana: { + alert: { + rule: { + execution: { + uuid: this.executionId, + }, + }, + }, saved_objects: [ { rel: SAVED_OBJECT_REL_PRIMARY, @@ -883,6 +897,7 @@ interface GenerateNewAndRecoveredAlertEventsParams< InstanceContext extends AlertInstanceContext > { eventLogger: IEventLogger; + executionId: string; originalAlerts: Dictionary>; currentAlerts: Dictionary>; recoveredAlerts: Dictionary>; @@ -911,6 +926,7 @@ function generateNewAndRecoveredAlertEvents< >(params: GenerateNewAndRecoveredAlertEventsParams) { const { eventLogger, + executionId, ruleId, namespace, currentAlerts, @@ -990,6 +1006,13 @@ function generateNewAndRecoveredAlertEvents< ...(state?.duration !== undefined ? { duration: state.duration as number } : {}), }, kibana: { + alert: { + rule: { + execution: { + uuid: executionId, + }, + }, + }, alerting: { instance_id: alertId, ...(group ? { action_group_id: group } : {}), diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts index 3e3c3351a8e67..e24be639c7fcc 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts @@ -32,6 +32,10 @@ import { Alert, RecoveredActionGroup } from '../../common'; import { UntypedNormalizedRuleType } from '../rule_type_registry'; import { ruleTypeRegistryMock } from '../rule_type_registry.mock'; +jest.mock('uuid', () => ({ + v4: () => '5f6aa57d-3e22-484e-bae8-cbed868f4d28', +})); + const ruleType: jest.Mocked = { id: 'test', name: 'My test rule', @@ -208,6 +212,13 @@ describe('Task Runner Cancel', () => { kind: 'alert', }, kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, saved_objects: [ { id: '1', @@ -236,6 +247,13 @@ describe('Task Runner Cancel', () => { kind: 'alert', }, kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, saved_objects: [ { id: '1', @@ -261,6 +279,13 @@ describe('Task Runner Cancel', () => { outcome: 'success', }, kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, alerting: { status: 'ok', }, @@ -437,6 +462,13 @@ describe('Task Runner Cancel', () => { kind: 'alert', }, kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, task: { schedule_delay: 0, scheduled: '1970-01-01T00:00:00.000Z', @@ -465,6 +497,13 @@ describe('Task Runner Cancel', () => { kind: 'alert', }, kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, saved_objects: [ { id: '1', @@ -491,6 +530,13 @@ describe('Task Runner Cancel', () => { outcome: 'success', }, kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, alerting: { status: 'active', }, @@ -553,6 +599,13 @@ describe('Task Runner Cancel', () => { kind: 'alert', }, kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, task: { schedule_delay: 0, scheduled: '1970-01-01T00:00:00.000Z', @@ -582,6 +635,13 @@ describe('Task Runner Cancel', () => { kind: 'alert', }, kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, saved_objects: [ { id: '1', @@ -609,6 +669,13 @@ describe('Task Runner Cancel', () => { start: '1970-01-01T00:00:00.000Z', }, kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, alerting: { action_group_id: 'default', instance_id: '1', @@ -642,6 +709,13 @@ describe('Task Runner Cancel', () => { start: '1970-01-01T00:00:00.000Z', }, kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, alerting: { action_group_id: 'default', instance_id: '1', @@ -666,6 +740,13 @@ describe('Task Runner Cancel', () => { kind: 'alert', }, kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, alerting: { instance_id: '1', action_group_id: 'default', @@ -697,6 +778,13 @@ describe('Task Runner Cancel', () => { expect(eventLogger.logEvent).toHaveBeenNthCalledWith(6, { event: { action: 'execute', category: ['alerts'], kind: 'alert', outcome: 'success' }, kibana: { + alert: { + rule: { + execution: { + uuid: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', + }, + }, + }, alerting: { status: 'active', }, diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index 866c8665ddb65..93ee520c7126a 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -90,6 +90,7 @@ export interface AlertExecutorOptions< ActionGroupIds extends string = never > { alertId: string; + executionId: string; startedAt: Date; previousStartedAt: Date | null; services: AlertServices; diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts index 4bbbb355d573e..a8289d5766cc5 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts @@ -51,6 +51,7 @@ const initialRuleState: TestRuleState = { const mockOptions = { alertId: '', + executionId: '', startedAt: new Date(), previousStartedAt: null, state: { diff --git a/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.ts b/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.ts index d0feac5f8aa32..c81329baad572 100644 --- a/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.ts +++ b/x-pack/plugins/rule_registry/common/assets/field_maps/technical_rule_field_map.ts @@ -107,6 +107,11 @@ export const technicalRuleFieldMap = { array: false, required: false, }, + [Fields.ALERT_RULE_EXECUTION_UUID]: { + type: 'keyword', + array: false, + required: false, + }, [Fields.ALERT_RULE_FROM]: { type: 'keyword', array: false, diff --git a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts index d822d9316ad19..05a71677c7535 100644 --- a/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts +++ b/x-pack/plugins/rule_registry/server/utils/create_lifecycle_rule_type.test.ts @@ -119,6 +119,7 @@ function createRule(shouldWriteAlerts: boolean = true) { tags: ['tags'], updatedBy: 'updatedBy', namespace: 'namespace', + executionId: 'b33f65d7-6e8b-4aae-8d20-c93613dec9f9', })) ?? {}) as Record; previousStartedAt = startedAt; @@ -224,6 +225,7 @@ describe('createLifecycleRuleTypeFactory', () => { "kibana.alert.instance.id": "opbeans-java", "kibana.alert.rule.category": "ruleTypeName", "kibana.alert.rule.consumer": "consumer", + "kibana.alert.rule.execution.uuid": "b33f65d7-6e8b-4aae-8d20-c93613dec9f9", "kibana.alert.rule.name": "name", "kibana.alert.rule.producer": "producer", "kibana.alert.rule.rule_type_id": "ruleTypeId", @@ -251,6 +253,7 @@ describe('createLifecycleRuleTypeFactory', () => { "kibana.alert.instance.id": "opbeans-node", "kibana.alert.rule.category": "ruleTypeName", "kibana.alert.rule.consumer": "consumer", + "kibana.alert.rule.execution.uuid": "b33f65d7-6e8b-4aae-8d20-c93613dec9f9", "kibana.alert.rule.name": "name", "kibana.alert.rule.producer": "producer", "kibana.alert.rule.rule_type_id": "ruleTypeId", diff --git a/x-pack/plugins/rule_registry/server/utils/get_common_alert_fields.ts b/x-pack/plugins/rule_registry/server/utils/get_common_alert_fields.ts index 8b4daf30763f6..db8c56f84b2c4 100644 --- a/x-pack/plugins/rule_registry/server/utils/get_common_alert_fields.ts +++ b/x-pack/plugins/rule_registry/server/utils/get_common_alert_fields.ts @@ -11,6 +11,7 @@ import { ALERT_UUID, ALERT_RULE_CATEGORY, ALERT_RULE_CONSUMER, + ALERT_RULE_EXECUTION_UUID, ALERT_RULE_NAME, ALERT_RULE_PRODUCER, ALERT_RULE_TYPE_ID, @@ -26,6 +27,7 @@ import { ParsedTechnicalFields } from '../../common/parse_technical_fields'; const commonAlertFieldNames = [ ALERT_RULE_CATEGORY, ALERT_RULE_CONSUMER, + ALERT_RULE_EXECUTION_UUID, ALERT_RULE_NAME, ALERT_RULE_PRODUCER, ALERT_RULE_TYPE_ID, @@ -47,6 +49,7 @@ export const getCommonAlertFields = ( return { [ALERT_RULE_CATEGORY]: options.rule.ruleTypeName, [ALERT_RULE_CONSUMER]: options.rule.consumer, + [ALERT_RULE_EXECUTION_UUID]: options.executionId, [ALERT_RULE_NAME]: options.rule.name, [ALERT_RULE_PRODUCER]: options.rule.producer, [ALERT_RULE_TYPE_ID]: options.rule.ruleTypeId, diff --git a/x-pack/plugins/rule_registry/server/utils/rule_executor_test_utils.ts b/x-pack/plugins/rule_registry/server/utils/rule_executor_test_utils.ts index 10cce043fe3fd..08b1b0a8ecbf2 100644 --- a/x-pack/plugins/rule_registry/server/utils/rule_executor_test_utils.ts +++ b/x-pack/plugins/rule_registry/server/utils/rule_executor_test_utils.ts @@ -79,4 +79,5 @@ export const createDefaultAlertExecutorOptions = < updatedBy: null, previousStartedAt: null, namespace: undefined, + executionId: 'b33f65d7-6e8b-4aae-8d20-c93613deb33f', }); diff --git a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts index 7b9dd63c73251..964819164b315 100644 --- a/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/detection_alerts/alerts_details.spec.ts @@ -57,7 +57,7 @@ describe('Alert details with unmapped fields', () => { // This test needs to be updated to not look for the field in a specific row, as it prevents us from adding/removing fields it.skip('Displays the unmapped field on the table', () => { const expectedUnmmappedField = { - row: 82, + row: 83, field: 'unmapped', text: 'This is the unmapped field', }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_rules_notification_alert_type.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_rules_notification_alert_type.test.ts index f064380cc4a13..1d97b7a39779a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_rules_notification_alert_type.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/notifications/legacy_rules_notification_alert_type.test.ts @@ -39,6 +39,7 @@ describe('legacyRules_notification_alert_type', () => { payload = { alertId: '1111', + executionId: 'b33f65d7-b33f-4aae-8d20-c93613dec9f9', services: alertServices, params: { ruleAlertId: '2222' }, state: {}, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/__snapshots__/get_signals_template.test.ts.snap b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/__snapshots__/get_signals_template.test.ts.snap index e03e438650df9..b826ed83d34ed 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/__snapshots__/get_signals_template.test.ts.snap +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/__snapshots__/get_signals_template.test.ts.snap @@ -4164,6 +4164,13 @@ Object { "enabled": Object { "type": "keyword", }, + "execution": Object { + "properties": Object { + "uuid": Object { + "type": "keyword", + }, + }, + }, "false_positives": Object { "type": "keyword", }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/signals_mapping.json b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/signals_mapping.json index 4f754ecd2d33a..6df246d06760d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/signals_mapping.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/index/signals_mapping.json @@ -286,6 +286,13 @@ "enabled": { "type": "keyword" }, + "execution": { + "properties": { + "uuid": { + "type": "keyword" + } + } + }, "filters": { "type": "object" }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts index 204c67bf6cf5a..263aa9e54737d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/preview_rules_route.ts @@ -170,6 +170,7 @@ export const previewRulesRoute = async ( statePreview = (await executor({ alertId: previewId, createdBy: rule.createdBy, + executionId: uuid.v4(), name: rule.name, params, previousStartedAt, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/__mocks__/rule_execution_log_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/__mocks__/rule_execution_log_client.ts index 22a41f356c226..7cf82722c47ff 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/__mocks__/rule_execution_log_client.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/__mocks__/rule_execution_log_client.ts @@ -23,6 +23,7 @@ const ruleExecutionLogClientMock = { const ruleExecutionLoggerMock = { create: (context: Partial = {}): jest.Mocked => ({ context: { + executionId: context.executionId ?? 'some execution id', ruleId: context.ruleId ?? 'some rule id', ruleName: context.ruleName ?? 'Some rule', ruleType: context.ruleType ?? 'some rule type', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_events/events_writer.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_events/events_writer.ts index dad30e9cb5d88..2869f1c2c82e8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_events/events_writer.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_events/events_writer.ts @@ -24,6 +24,7 @@ export interface IRuleExecutionEventsWriter { } export interface BaseArgs { + executionId: string; ruleId: string; ruleName: string; ruleType: string; @@ -49,7 +50,7 @@ export const createRuleExecutionEventsWriter = ( let sequence = 0; return { - logStatusChange({ ruleId, ruleName, ruleType, spaceId, newStatus, message }) { + logStatusChange({ executionId, ruleId, ruleName, ruleType, spaceId, newStatus, message }) { eventLogger.logEvent({ '@timestamp': nowISO(), message, @@ -69,6 +70,7 @@ export const createRuleExecutionEventsWriter = ( execution: { status: newStatus, status_order: ruleExecutionStatusOrderByStatus[newStatus], + uuid: executionId, }, }, }, @@ -85,7 +87,7 @@ export const createRuleExecutionEventsWriter = ( }); }, - logExecutionMetrics({ ruleId, ruleName, ruleType, spaceId, metrics }) { + logExecutionMetrics({ executionId, ruleId, ruleName, ruleType, spaceId, metrics }) { eventLogger.logEvent({ '@timestamp': nowISO(), rule: { @@ -103,6 +105,7 @@ export const createRuleExecutionEventsWriter = ( rule: { execution: { metrics, + uuid: executionId, }, }, }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_logger/logger.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_logger/logger.ts index f67aae472ef60..a3e14f9569e25 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_logger/logger.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_logger/logger.ts @@ -28,7 +28,7 @@ export const createRuleExecutionLogger = ( logger: Logger, context: RuleExecutionContext ): IRuleExecutionLogger => { - const { ruleId, ruleName, ruleType, spaceId } = context; + const { executionId, ruleId, ruleName, ruleType, spaceId } = context; const ruleExecutionLogger: IRuleExecutionLogger = { get context() { @@ -44,7 +44,7 @@ export const createRuleExecutionLogger = ( ]); } catch (e) { const logMessage = 'Error logging rule execution status change'; - const logAttributes = `status: "${args.newStatus}", rule id: "${ruleId}", rule name: "${ruleName}"`; + const logAttributes = `status: "${args.newStatus}", rule id: "${ruleId}", rule name: "${ruleName}", execution uuid: "${executionId}"`; const logReason = e instanceof Error ? e.stack ?? e.message : String(e); const logMeta: ExtMeta = { rule: { @@ -53,6 +53,7 @@ export const createRuleExecutionLogger = ( type: ruleType, execution: { status: args.newStatus, + uuid: executionId, }, }, kibana: { @@ -65,6 +66,7 @@ export const createRuleExecutionLogger = ( }, }; + // TODO: Add executionId to new status SO? const writeStatusChangeToSavedObjects = async ( args: NormalizedStatusChangeArgs ): Promise => { @@ -86,6 +88,7 @@ export const createRuleExecutionLogger = ( if (metrics) { eventsWriter.logExecutionMetrics({ + executionId, ruleId, ruleName, ruleType, @@ -95,6 +98,7 @@ export const createRuleExecutionLogger = ( } eventsWriter.logStatusChange({ + executionId, ruleId, ruleName, ruleType, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_logger/logger_interface.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_logger/logger_interface.ts index e31c10bd9747f..874d60cf4a401 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_logger/logger_interface.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/rule_execution_logger/logger_interface.ts @@ -29,6 +29,7 @@ export interface IRuleExecutionLogger { } export interface RuleExecutionContext { + executionId: string; ruleId: string; ruleName: string; ruleType: string; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts index a643f428e58e7..ba30b0335fb39 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/create_security_rule_type_wrapper.ts @@ -60,6 +60,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = return withSecuritySpan('scurityRuleTypeExecutor', async () => { const { alertId, + executionId, params, previousStartedAt, startedAt, @@ -81,6 +82,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = eventLogService, logger, { + executionId, ruleId: alertId, ruleName: rule.name, ruleType: rule.ruleTypeId, @@ -104,6 +106,7 @@ export const createSecurityRuleTypeWrapper: CreateSecurityRuleTypeWrapper = const buildRuleMessage = buildRuleMessageFactory({ id: alertId, + executionId, ruleId, name, index: spaceId, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/build_rule_message_factory.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/build_rule_message_factory.ts index 6ebc902db6992..bac112bb3cab1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/build_rule_message_factory.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/build_rule_message_factory.ts @@ -7,6 +7,7 @@ export type BuildRuleMessage = (...messages: string[]) => string; export interface BuildRuleMessageFactoryParams { + executionId: string; name: string; id: string; ruleId: string | null | undefined; @@ -15,12 +16,13 @@ export interface BuildRuleMessageFactoryParams { // TODO: change `index` param to `spaceId` export const buildRuleMessageFactory = - ({ id, ruleId, index, name }: BuildRuleMessageFactoryParams): BuildRuleMessage => + ({ executionId, id, ruleId, index, name }: BuildRuleMessageFactoryParams): BuildRuleMessage => (...messages) => [ ...messages, `name: "${name}"`, `id: "${id}"`, `rule id: "${ruleId ?? '(unknown rule id)'}"`, + `execution id: "${executionId}"`, `space ID: "${index}"`, ].join(' '); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts index 88b4ae01b3a64..07f2dfa31015c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts @@ -93,6 +93,8 @@ export const buildAncestors = (doc: SimpleHit): Ancestor[] => { * Builds the `kibana.alert.*` fields that are common across all alerts. * @param docs The parent alerts/events of the new alert to be built. * @param rule The rule that is generating the new alert. + * @param spaceId The space ID in which the rule was executed. + * @param reason Human readable string summarizing alert. */ export const buildAlert = ( docs: SimpleHit[], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts index c620d51a83382..cae1019ae3f80 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/utils.test.ts @@ -663,7 +663,6 @@ describe('utils', () => { }, }, }; - const ruleExecutionLogger = ruleExecutionLogMock.logger.create(); mockLogger.warn.mockClear(); diff --git a/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.test.ts b/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.test.ts index 6b424adaa54ab..c0d05c44201fb 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.test.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/es_query/alert_type.test.ts @@ -140,6 +140,7 @@ describe('alertType', () => { const result = await alertType.executor({ alertId: uuid.v4(), + executionId: uuid.v4(), startedAt: new Date(), previousStartedAt: new Date(), services: alertServices as unknown as AlertServices< @@ -218,6 +219,7 @@ describe('alertType', () => { const result = await alertType.executor({ alertId: uuid.v4(), + executionId: uuid.v4(), startedAt: new Date(), previousStartedAt: new Date(), services: alertServices as unknown as AlertServices< @@ -369,6 +371,7 @@ describe('alertType', () => { const result = await alertType.executor({ alertId: uuid.v4(), + executionId: uuid.v4(), startedAt: new Date(), previousStartedAt: new Date(), services: alertServices as unknown as AlertServices< @@ -446,6 +449,7 @@ describe('alertType', () => { const executorOptions = { alertId: uuid.v4(), + executionId: uuid.v4(), startedAt: new Date(), previousStartedAt: new Date(), services: alertServices as unknown as AlertServices< @@ -559,6 +563,7 @@ describe('alertType', () => { const result = await alertType.executor({ alertId: uuid.v4(), + executionId: uuid.v4(), startedAt: new Date(), previousStartedAt: new Date(), services: alertServices as unknown as AlertServices< @@ -642,6 +647,7 @@ describe('alertType', () => { const result = await alertType.executor({ alertId: uuid.v4(), + executionId: uuid.v4(), startedAt: new Date(), previousStartedAt: new Date(), services: alertServices as unknown as AlertServices< diff --git a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.test.ts b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.test.ts index 7ab382ec77172..f6b1c4a3a3b0a 100644 --- a/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.test.ts +++ b/x-pack/plugins/stack_alerts/server/alert_types/index_threshold/alert_type.test.ts @@ -169,6 +169,7 @@ describe('alertType', () => { await alertType.executor({ alertId: uuid.v4(), + executionId: uuid.v4(), startedAt: new Date(), previousStartedAt: new Date(), services: alertServices as unknown as AlertServices<{}, ActionContext, typeof ActionGroupId>, @@ -230,6 +231,7 @@ describe('alertType', () => { await alertType.executor({ alertId: uuid.v4(), + executionId: uuid.v4(), startedAt: new Date(), previousStartedAt: new Date(), services: customAlertServices as unknown as AlertServices< @@ -295,6 +297,7 @@ describe('alertType', () => { await alertType.executor({ alertId: uuid.v4(), + executionId: uuid.v4(), startedAt: new Date(), previousStartedAt: new Date(), services: customAlertServices as unknown as AlertServices< diff --git a/x-pack/test/alerting_api_integration/common/lib/get_event_log.ts b/x-pack/test/alerting_api_integration/common/lib/get_event_log.ts index a4cc9d5139148..3ee7929170338 100644 --- a/x-pack/test/alerting_api_integration/common/lib/get_event_log.ts +++ b/x-pack/test/alerting_api_integration/common/lib/get_event_log.ts @@ -16,7 +16,7 @@ interface EqualCondition { equal: number; } -function isEqualConsition( +function isEqualCondition( condition: GreaterThanEqualCondition | EqualCondition ): condition is EqualCondition { return Number.isInteger((condition as EqualCondition).equal); @@ -67,7 +67,7 @@ export async function getEventLog(params: GetEventLogParams): Promise= condition.gte) ) @@ -76,7 +76,7 @@ export async function getEventLog(params: GetEventLogParams): Promise event?.event?.action !== 'execute' ); + // Verify unique executionId generated per `action:execute` grouping + const eventExecutionIdSet = new Set(); + const totalUniqueExecutionIds = new Set(); + let totalExecutionEventCount = 0; + events.forEach((event) => { + totalUniqueExecutionIds.add(event?.kibana?.alert?.rule?.execution?.uuid); + if (event?.event?.action === 'execute') { + totalExecutionEventCount += 1; + eventExecutionIdSet.add(event?.kibana?.alert?.rule?.execution?.uuid); + expect(eventExecutionIdSet.size).to.equal(1); + eventExecutionIdSet.clear(); + } else { + eventExecutionIdSet.add(event?.kibana?.alert?.rule?.execution?.uuid); + } + }); + + // Ensure every execution actually had a unique id from the others + expect(totalUniqueExecutionIds.size).to.equal(totalExecutionEventCount); + const currentAlertSpan: { alertId?: string; start?: string; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts index cb1c41852465f..343db03c2ae27 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/create_ml.ts @@ -8,6 +8,7 @@ import expect from '@kbn/expect'; import { ALERT_REASON, + ALERT_RULE_EXECUTION_UUID, ALERT_RULE_NAMESPACE, ALERT_RULE_PARAMETERS, ALERT_RULE_UPDATED_AT, @@ -120,6 +121,7 @@ export default ({ getService }: FtrProviderContext) => { expect(signal._source).eql({ '@timestamp': signal._source['@timestamp'], + [ALERT_RULE_EXECUTION_UUID]: signal._source[ALERT_RULE_EXECUTION_UUID], [ALERT_UUID]: signal._source[ALERT_UUID], [VERSION]: signal._source[VERSION], actual: [1], diff --git a/x-pack/test/rule_registry/spaces_only/tests/trial/create_rule.ts b/x-pack/test/rule_registry/spaces_only/tests/trial/create_rule.ts index ac36bad1f595b..be3146f34c30e 100644 --- a/x-pack/test/rule_registry/spaces_only/tests/trial/create_rule.ts +++ b/x-pack/test/rule_registry/spaces_only/tests/trial/create_rule.ts @@ -9,6 +9,7 @@ import expect from '@kbn/expect'; import { ALERT_DURATION, ALERT_END, + ALERT_RULE_EXECUTION_UUID, ALERT_RULE_UUID, ALERT_START, ALERT_STATUS, @@ -187,7 +188,14 @@ export default function registryRulesApiTest({ getService }: FtrProviderContext) const alertEvent = afterViolatingDataResponse.hits.hits[0].fields as Record; - const exclude = ['@timestamp', ALERT_START, ALERT_UUID, ALERT_RULE_UUID, VERSION]; + const exclude = [ + '@timestamp', + ALERT_START, + ALERT_UUID, + ALERT_RULE_EXECUTION_UUID, + ALERT_RULE_UUID, + VERSION, + ]; const toCompare = omit(alertEvent, exclude);