From 84f085e7a235412327b8302d085b08590f04fe17 Mon Sep 17 00:00:00 2001 From: Georgii Gorbachev Date: Mon, 18 Oct 2021 17:27:50 +0200 Subject: [PATCH 1/2] [Security Solution] Migrates siem-detection-engine-rule-status alertId to saved object references array (#114585) (#115359) ## Summary Resolves (a portion of) https://github.com/elastic/kibana/issues/107068 for the `siem-detection-engine-rule-status` type by migrating the `alertId` to be within the `SO references[]`. Based on: https://github.com/elastic/kibana/pull/113577 * Migrates the legacy `siem-detection-engine-rule-status` `alertId` to saved object references array * Adds an e2e test for `siem-detection-engine-rule-status` * Breaks out `siem-detection-engine-rule-status` & `security-rule` SO's to their own dedicated files/directories, and cleaned up typings/imports Before migration you can observe the existing data structure of `siem-detection-engine-rule-status` via Dev tools as follows: ``` GET .kibana/_search { "size": 10000, "query": { "term": { "type": { "value": "siem-detection-engine-rule-status" } } } } ``` ``` JSON { "_index" : ".kibana-spong_8.0.0_001", "_id" : "siem-detection-engine-rule-status:d580f1a0-2afe-11ec-8621-8d6bfcdfd75e", "_score" : 2.150102, "_source" : { "siem-detection-engine-rule-status" : { "alertId" : "d62d2980-27c4-11ec-92b0-f7b47106bb35", <-- alertId which we want in the references array and removed "statusDate" : "2021-10-12T01:50:52.898Z", "status" : "failed", "lastFailureAt" : "2021-10-12T01:50:52.898Z", "lastSuccessAt" : "2021-10-12T01:18:29.195Z", "lastFailureMessage" : "6 minutes (385585ms) were not queried between this rule execution and the last execution, so signals may have been missed. Consider increasing your look behind time or adding more Kibana instances. name: \"I am the Host who Names!\" id: \"d62d2980-27c4-11ec-92b0-f7b47106bb35\" rule id: \"214ccef6-e98e-493a-98c5-5bcc2d497b79\" signals index: \".siem-signals-spong-default\"", "lastSuccessMessage" : "succeeded", "gap" : "6 minutes", "lastLookBackDate" : "2021-10-07T23:43:27.961Z" }, "type" : "siem-detection-engine-rule-status", "references" : [ ], "coreMigrationVersion" : "7.14.0", "updated_at" : "2021-10-12T01:50:53.404Z" } } ``` Post migration the data structure should be updated as follows: ``` JSON { "_index": ".kibana-spong_8.0.0_001", "_id": "siem-detection-engine-rule-status:d580f1a0-2afe-11ec-8621-8d6bfcdfd75e", "_score": 2.1865466, "_source": { "siem-detection-engine-rule-status": { "statusDate": "2021-10-12T01:50:52.898Z", <-- alertId is no more! "status": "failed", "lastFailureAt": "2021-10-12T01:50:52.898Z", "lastSuccessAt": "2021-10-12T01:18:29.195Z", "lastFailureMessage": "6 minutes (385585ms) were not queried between this rule execution and the last execution, so signals may have been missed. Consider increasing your look behind time or adding more Kibana instances. name: \"I am the Host who Names!\" id: \"d62d2980-27c4-11ec-92b0-f7b47106bb35\" rule id: \"214ccef6-e98e-493a-98c5-5bcc2d497b79\" signals index: \".siem-signals-spong-default\"", "lastSuccessMessage": "succeeded", "gap": "6 minutes", "lastLookBackDate": "2021-10-07T23:43:27.961Z" }, "type": "siem-detection-engine-rule-status", "references": [ { "id": "d62d2980-27c4-11ec-92b0-f7b47106bb35", <-- previous alertId has been converted to references[] "type": "alert", "name": "alert_0" } ], "migrationVersion": { "siem-detection-engine-rule-status": "7.16.0" }, "coreMigrationVersion": "8.0.0", "updated_at": "2021-10-12T01:50:53.406Z" } }, ``` #### Manual testing --- There are e2e tests but for any manual testing or verification you can do the following: ##### Manual upgrade test If you have a 7.15.0 system and can migrate it forward that is the most straight forward way to ensure this does migrate correctly. You should see that the `Rule Monitoring` table and Rule Details `Failure History` table continue to function without error. ##### Downgrade via script and test migration on kibana reboot If you have a migrated `Rule Status SO` and want to test the migration, you can run the below script to downgrade the status SO then restart Kibana and observe the migration on startup. Note: Since this PR removes the mapping, you would need to [update the SO mapping](https://github.com/elastic/kibana/pull/114585/files#r729386126) to include `alertId` again else you will receive a strict/dynamic mapping error. ```json # Replace id w/ correct Rule Status SO id of existing migrated object POST .kibana/_update/siem-detection-engine-rule-status:d580ca91-2afe-11ec-8621-8d6bfcdfd75e { "script" : { "source": """ ctx._source.migrationVersion['siem-detection-engine-rule-status'] = "7.15.0"; ctx._source['siem-detection-engine-rule-status'].alertId = ctx._source.references[0].id; ctx._source.references.remove(0); """, "lang": "painless" } } ``` Restart Kibana and now it should be migrated correctly and you shouldn't see any errors in your console. You should also see that the `Rule Monitoring` table and Rule Details `Failure History` table continue to function without error. ### Checklist Delete any items that are not applicable to this PR. - [ ] ~[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials~ - [X] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) Co-authored-by: Georgii Gorbachev Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Garrett Spong Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../aggregations/aggs_types/bucket_aggs.ts | 5 +- .../routes/__mocks__/request_responses.ts | 20 ++- .../rules/add_prepackaged_rules_route.ts | 2 +- .../get_prepackaged_rules_status_route.ts | 2 +- .../lib/detection_engine/routes/utils.test.ts | 8 +- .../event_log_adapter/event_log_adapter.ts | 4 +- .../rule_status_saved_objects_client.ts | 87 ++++++++----- .../saved_objects_adapter.ts | 72 +++++++---- .../rule_execution_log/types.ts | 1 + .../rules/delete_rules.test.ts | 1 - .../lib/detection_engine/rules/enable_rule.ts | 1 + .../rules/get_prepackaged_rules.ts | 2 +- .../legacy_rule_status/legacy_migrations.ts | 115 ++++++++++++++++++ ...egacy_rule_status_saved_object_mappings.ts | 73 +++++++++++ .../rules/legacy_rule_status/legacy_utils.ts | 17 +++ .../rule_asset_saved_object_mappings.ts | 32 +++++ .../rule_asset_saved_objects_client.ts | 6 +- .../rules/saved_object_mappings.ts | 97 --------------- .../lib/detection_engine/rules/types.ts | 2 - .../signals/__mocks__/es_results.ts | 14 ++- .../security_solution/server/saved_objects.ts | 9 +- .../security_and_spaces/tests/migrations.ts | 27 ++++ .../security_solution/migrations/data.json | 34 +++++- 23 files changed, 441 insertions(+), 190 deletions(-) create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/legacy_rule_status/legacy_migrations.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/legacy_rule_status/legacy_rule_status_saved_object_mappings.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/legacy_rule_status/legacy_utils.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/rule_asset/rule_asset_saved_object_mappings.ts rename x-pack/plugins/security_solution/server/lib/detection_engine/rules/{ => rule_asset}/rule_asset_saved_objects_client.ts (88%) delete mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/rules/saved_object_mappings.ts diff --git a/src/core/server/saved_objects/service/lib/aggregations/aggs_types/bucket_aggs.ts b/src/core/server/saved_objects/service/lib/aggregations/aggs_types/bucket_aggs.ts index cf27505e8f073..f85576aa64451 100644 --- a/src/core/server/saved_objects/service/lib/aggregations/aggs_types/bucket_aggs.ts +++ b/src/core/server/saved_objects/service/lib/aggregations/aggs_types/bucket_aggs.ts @@ -16,6 +16,7 @@ import { sortOrderSchema } from './common_schemas'; * - filter * - histogram * - nested + * - reverse_nested * - terms * * Not implemented: @@ -37,7 +38,6 @@ import { sortOrderSchema } from './common_schemas'; * - parent * - range * - rare_terms - * - reverse_nested * - sampler * - significant_terms * - significant_text @@ -76,6 +76,9 @@ export const bucketAggsSchemas: Record = { nested: s.object({ path: s.string(), }), + reverse_nested: s.object({ + path: s.maybe(s.string()), + }), terms: s.object({ field: s.maybe(s.string()), collect_mode: s.maybe(s.string()), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts index 200246ba1a367..9d1cd3cbca3fb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/__mocks__/request_responses.ts @@ -479,7 +479,6 @@ export const getRuleExecutionStatuses = (): Array< type: 'my-type', id: 'e0b86950-4e9f-11ea-bdbd-07b56aa159b3', attributes: { - alertId: '04128c15-0d1b-4716-a4c5-46997ac7f3bc', statusDate: '2020-02-18T15:26:49.783Z', status: RuleExecutionStatus.succeeded, lastFailureAt: undefined, @@ -492,7 +491,13 @@ export const getRuleExecutionStatuses = (): Array< bulkCreateTimeDurations: ['800.43'], }, score: 1, - references: [], + references: [ + { + id: '04128c15-0d1b-4716-a4c5-46997ac7f3bc', + type: 'alert', + name: 'alert_0', + }, + ], updated_at: '2020-02-18T15:26:51.333Z', version: 'WzQ2LDFd', }, @@ -500,7 +505,6 @@ export const getRuleExecutionStatuses = (): Array< type: 'my-type', id: '91246bd0-5261-11ea-9650-33b954270f67', attributes: { - alertId: '1ea5a820-4da1-4e82-92a1-2b43a7bece08', statusDate: '2020-02-18T15:15:58.806Z', status: RuleExecutionStatus.failed, lastFailureAt: '2020-02-18T15:15:58.806Z', @@ -514,7 +518,13 @@ export const getRuleExecutionStatuses = (): Array< bulkCreateTimeDurations: ['800.43'], }, score: 1, - references: [], + references: [ + { + id: '1ea5a820-4da1-4e82-92a1-2b43a7bece08', + type: 'alert', + name: 'alert_0', + }, + ], updated_at: '2020-02-18T15:15:58.860Z', version: 'WzMyLDFd', }, @@ -523,7 +533,6 @@ export const getRuleExecutionStatuses = (): Array< export const getFindBulkResultStatus = (): FindBulkExecutionLogResponse => ({ '04128c15-0d1b-4716-a4c5-46997ac7f3bd': [ { - alertId: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', statusDate: '2020-02-18T15:26:49.783Z', status: RuleExecutionStatus.succeeded, lastFailureAt: undefined, @@ -538,7 +547,6 @@ export const getFindBulkResultStatus = (): FindBulkExecutionLogResponse => ({ ], '1ea5a820-4da1-4e82-92a1-2b43a7bece08': [ { - alertId: '1ea5a820-4da1-4e82-92a1-2b43a7bece08', statusDate: '2020-02-18T15:15:58.806Z', status: RuleExecutionStatus.failed, lastFailureAt: '2020-02-18T15:15:58.806Z', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts index 0048c735b0a7c..fed34743e220a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/add_prepackaged_rules_route.ts @@ -31,7 +31,7 @@ import { updatePrepackagedRules } from '../../rules/update_prepacked_rules'; import { getRulesToInstall } from '../../rules/get_rules_to_install'; import { getRulesToUpdate } from '../../rules/get_rules_to_update'; import { getExistingPrepackagedRules } from '../../rules/get_existing_prepackaged_rules'; -import { ruleAssetSavedObjectsClientFactory } from '../../rules/rule_asset_saved_objects_client'; +import { ruleAssetSavedObjectsClientFactory } from '../../rules/rule_asset/rule_asset_saved_objects_client'; import { buildSiemResponse } from '../utils'; import { RulesClient } from '../../../../../../alerting/server'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts index 9a06928eee233..a18507eea4977 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/get_prepackaged_rules_status_route.ts @@ -20,7 +20,7 @@ import { getRulesToUpdate } from '../../rules/get_rules_to_update'; import { findRules } from '../../rules/find_rules'; import { getLatestPrepackagedRules } from '../../rules/get_prepackaged_rules'; import { getExistingPrepackagedRules } from '../../rules/get_existing_prepackaged_rules'; -import { ruleAssetSavedObjectsClientFactory } from '../../rules/rule_asset_saved_objects_client'; +import { ruleAssetSavedObjectsClientFactory } from '../../rules/rule_asset/rule_asset_saved_objects_client'; import { buildFrameworkRequest } from '../../../timeline/utils/common'; import { ConfigType } from '../../../../config'; import { SetupPlugins } from '../../../../plugin'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/utils.test.ts index 10472fe1c0a03..6ddeeaa5ea1c2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/utils.test.ts @@ -136,16 +136,16 @@ describe.each([ describe('mergeStatuses', () => { it('merges statuses and converts from camelCase saved object to snake_case HTTP response', () => { + // const statusOne = exampleRuleStatus(); statusOne.attributes.status = RuleExecutionStatus.failed; const statusTwo = exampleRuleStatus(); statusTwo.attributes.status = RuleExecutionStatus.failed; const currentStatus = exampleRuleStatus(); const foundRules = [currentStatus.attributes, statusOne.attributes, statusTwo.attributes]; - const res = mergeStatuses(currentStatus.attributes.alertId, foundRules, { + const res = mergeStatuses(currentStatus.references[0].id, foundRules, { 'myfakealertid-8cfac': { current_status: { - alert_id: 'myfakealertid-8cfac', status_date: '2020-03-27T22:55:59.517Z', status: RuleExecutionStatus.succeeded, last_failure_at: null, @@ -163,7 +163,6 @@ describe.each([ expect(res).toEqual({ 'myfakealertid-8cfac': { current_status: { - alert_id: 'myfakealertid-8cfac', status_date: '2020-03-27T22:55:59.517Z', status: 'succeeded', last_failure_at: null, @@ -179,7 +178,6 @@ describe.each([ }, 'f4b8e31d-cf93-4bde-a265-298bde885cd7': { current_status: { - alert_id: 'f4b8e31d-cf93-4bde-a265-298bde885cd7', status_date: '2020-03-27T22:55:59.517Z', status: 'succeeded', last_failure_at: null, @@ -193,7 +191,6 @@ describe.each([ }, failures: [ { - alert_id: 'f4b8e31d-cf93-4bde-a265-298bde885cd7', status_date: '2020-03-27T22:55:59.517Z', status: 'failed', last_failure_at: null, @@ -206,7 +203,6 @@ describe.each([ last_look_back_date: null, // NOTE: This is no longer used on the UI, but left here in case users are using it within the API }, { - alert_id: 'f4b8e31d-cf93-4bde-a265-298bde885cd7', status_date: '2020-03-27T22:55:59.517Z', status: 'failed', last_failure_at: null, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_adapter.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_adapter.ts index 086cc12788a40..a3fb50f1f6b0b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_adapter.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/event_log_adapter/event_log_adapter.ts @@ -42,7 +42,7 @@ export class EventLogAdapter implements IRuleExecutionLogClient { } public async update(args: UpdateExecutionLogArgs) { - const { attributes, spaceId, ruleName, ruleType } = args; + const { attributes, spaceId, ruleId, ruleName, ruleType } = args; await this.savedObjectsAdapter.update(args); @@ -51,7 +51,7 @@ export class EventLogAdapter implements IRuleExecutionLogClient { this.eventLogClient.logStatusChange({ ruleName, ruleType, - ruleId: attributes.alertId, + ruleId, newStatus: attributes.status, spaceId, }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/saved_objects_adapter/rule_status_saved_objects_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/saved_objects_adapter/rule_status_saved_objects_client.ts index 720659b72194f..66b646e96ea53 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/saved_objects_adapter/rule_status_saved_objects_client.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/saved_objects_adapter/rule_status_saved_objects_client.ts @@ -5,27 +5,33 @@ * 2.0. */ -import { get } from 'lodash'; import { - SavedObjectsClientContract, SavedObject, - SavedObjectsUpdateResponse, + SavedObjectsClientContract, + SavedObjectsCreateOptions, SavedObjectsFindOptions, + SavedObjectsFindOptionsReference, SavedObjectsFindResult, -} from '../../../../../../../../src/core/server'; -import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings'; + SavedObjectsUpdateResponse, +} from 'kibana/server'; +import { get } from 'lodash'; +// eslint-disable-next-line no-restricted-imports +import { legacyRuleStatusSavedObjectType } from '../../rules/legacy_rule_status/legacy_rule_status_saved_object_mappings'; import { IRuleStatusSOAttributes } from '../../rules/types'; -import { buildChunkedOrFilter } from '../../signals/utils'; export interface RuleStatusSavedObjectsClient { find: ( options?: Omit ) => Promise>>; findBulk: (ids: string[], statusesPerId: number) => Promise; - create: (attributes: IRuleStatusSOAttributes) => Promise>; + create: ( + attributes: IRuleStatusSOAttributes, + options: SavedObjectsCreateOptions + ) => Promise>; update: ( id: string, - attributes: Partial + attributes: Partial, + options: SavedObjectsCreateOptions ) => Promise>; delete: (id: string) => Promise<{}>; } @@ -35,7 +41,7 @@ export interface FindBulkResponse { } /** - * @pdeprecated Use RuleExecutionLogClient instead + * @deprecated Use RuleExecutionLogClient instead */ export const ruleStatusSavedObjectsClientFactory = ( savedObjectsClient: SavedObjectsClientContract @@ -43,7 +49,7 @@ export const ruleStatusSavedObjectsClientFactory = ( find: async (options) => { const result = await savedObjectsClient.find({ ...options, - type: ruleStatusSavedObjectType, + type: legacyRuleStatusSavedObjectType, }); return result.saved_objects; }, @@ -51,47 +57,64 @@ export const ruleStatusSavedObjectsClientFactory = ( if (ids.length === 0) { return {}; } - const filter = buildChunkedOrFilter(`${ruleStatusSavedObjectType}.attributes.alertId`, ids); + const references = ids.map((alertId) => ({ + id: alertId, + type: 'alert', + })); const order: 'desc' = 'desc'; const aggs = { - alertIds: { - terms: { - field: `${ruleStatusSavedObjectType}.attributes.alertId`, - size: ids.length, + references: { + nested: { + path: `${legacyRuleStatusSavedObjectType}.references`, }, aggs: { - most_recent_statuses: { - top_hits: { - sort: [ - { - [`${ruleStatusSavedObjectType}.statusDate`]: { - order, + alertIds: { + terms: { + field: `${legacyRuleStatusSavedObjectType}.references.id`, + size: ids.length, + }, + aggs: { + rule_status: { + reverse_nested: {}, + aggs: { + most_recent_statuses: { + top_hits: { + sort: [ + { + [`${legacyRuleStatusSavedObjectType}.statusDate`]: { + order, + }, + }, + ], + size: statusesPerId, + }, }, }, - ], - size: statusesPerId, + }, }, }, }, }, }; const results = await savedObjectsClient.find({ - filter, + hasReference: references, aggs, - type: ruleStatusSavedObjectType, + type: legacyRuleStatusSavedObjectType, perPage: 0, }); - const buckets = get(results, 'aggregations.alertIds.buckets'); + const buckets = get(results, 'aggregations.references.alertIds.buckets'); return buckets.reduce((acc: Record, bucket: unknown) => { const key = get(bucket, 'key'); - const hits = get(bucket, 'most_recent_statuses.hits.hits'); + const hits = get(bucket, 'rule_status.most_recent_statuses.hits.hits'); // eslint-disable-next-line @typescript-eslint/no-explicit-any - const statuses = hits.map((hit: any) => hit._source['siem-detection-engine-rule-status']); - acc[key] = statuses; + acc[key] = hits.map((hit: any) => hit._source[legacyRuleStatusSavedObjectType]); return acc; }, {}); }, - create: (attributes) => savedObjectsClient.create(ruleStatusSavedObjectType, attributes), - update: (id, attributes) => savedObjectsClient.update(ruleStatusSavedObjectType, id, attributes), - delete: (id) => savedObjectsClient.delete(ruleStatusSavedObjectType, id), + create: (attributes, options) => { + return savedObjectsClient.create(legacyRuleStatusSavedObjectType, attributes, options); + }, + update: (id, attributes, options) => + savedObjectsClient.update(legacyRuleStatusSavedObjectType, id, attributes, options), + delete: (id) => savedObjectsClient.delete(legacyRuleStatusSavedObjectType, id), }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/saved_objects_adapter/saved_objects_adapter.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/saved_objects_adapter/saved_objects_adapter.ts index ca806bd58e369..9db7afce62ee4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/saved_objects_adapter/saved_objects_adapter.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/saved_objects_adapter/saved_objects_adapter.ts @@ -5,9 +5,12 @@ * 2.0. */ -import { SavedObject } from 'src/core/server'; +import { SavedObject, SavedObjectReference } from 'src/core/server'; import { SavedObjectsClientContract } from '../../../../../../../../src/core/server'; import { RuleExecutionStatus } from '../../../../../common/detection_engine/schemas/common/schemas'; +// eslint-disable-next-line no-restricted-imports +import { legacyGetRuleReference } from '../../rules/legacy_rule_status/legacy_utils'; + import { IRuleStatusSOAttributes } from '../../rules/types'; import { RuleStatusSavedObjectsClient, @@ -51,7 +54,7 @@ export class SavedObjectsAdapter implements IRuleExecutionLogClient { sortField: 'statusDate', sortOrder: 'desc', search: ruleId, - searchFields: ['alertId'], + searchFields: ['references.id'], }); } @@ -59,8 +62,9 @@ export class SavedObjectsAdapter implements IRuleExecutionLogClient { return this.ruleStatusClient.findBulk(ruleIds, logsCount); } - public async update({ id, attributes }: UpdateExecutionLogArgs) { - await this.ruleStatusClient.update(id, attributes); + public async update({ id, attributes, ruleId }: UpdateExecutionLogArgs) { + const references: SavedObjectReference[] = [legacyGetRuleReference(ruleId)]; + await this.ruleStatusClient.update(id, attributes, { references }); } public async delete(id: string) { @@ -68,31 +72,39 @@ export class SavedObjectsAdapter implements IRuleExecutionLogClient { } public async logExecutionMetrics({ ruleId, metrics }: LogExecutionMetricsArgs) { + const references: SavedObjectReference[] = [legacyGetRuleReference(ruleId)]; const [currentStatus] = await this.getOrCreateRuleStatuses(ruleId); - await this.ruleStatusClient.update(currentStatus.id, { - ...currentStatus.attributes, - ...convertMetricFields(metrics), - }); + await this.ruleStatusClient.update( + currentStatus.id, + { + ...currentStatus.attributes, + ...convertMetricFields(metrics), + }, + { references } + ); } private createNewRuleStatus = async ( ruleId: string ): Promise> => { + const references: SavedObjectReference[] = [legacyGetRuleReference(ruleId)]; const now = new Date().toISOString(); - return this.ruleStatusClient.create({ - alertId: ruleId, - statusDate: now, - status: RuleExecutionStatus['going to run'], - lastFailureAt: null, - lastSuccessAt: null, - lastFailureMessage: null, - lastSuccessMessage: null, - gap: null, - bulkCreateTimeDurations: [], - searchAfterTimeDurations: [], - lastLookBackDate: null, - }); + return this.ruleStatusClient.create( + { + statusDate: now, + status: RuleExecutionStatus['going to run'], + lastFailureAt: null, + lastSuccessAt: null, + lastFailureMessage: null, + lastSuccessMessage: null, + gap: null, + bulkCreateTimeDurations: [], + searchAfterTimeDurations: [], + lastLookBackDate: null, + }, + { references } + ); }; private getOrCreateRuleStatuses = async ( @@ -112,6 +124,8 @@ export class SavedObjectsAdapter implements IRuleExecutionLogClient { }; public async logStatusChange({ newStatus, ruleId, message, metrics }: LogStatusChangeArgs) { + const references: SavedObjectReference[] = [legacyGetRuleReference(ruleId)]; + switch (newStatus) { case RuleExecutionStatus['going to run']: case RuleExecutionStatus.succeeded: @@ -119,10 +133,14 @@ export class SavedObjectsAdapter implements IRuleExecutionLogClient { case RuleExecutionStatus['partial failure']: { const [currentStatus] = await this.getOrCreateRuleStatuses(ruleId); - await this.ruleStatusClient.update(currentStatus.id, { - ...currentStatus.attributes, - ...buildRuleStatusAttributes(newStatus, message, metrics), - }); + await this.ruleStatusClient.update( + currentStatus.id, + { + ...currentStatus.attributes, + ...buildRuleStatusAttributes(newStatus, message, metrics), + }, + { references } + ); return; } @@ -137,8 +155,8 @@ export class SavedObjectsAdapter implements IRuleExecutionLogClient { }; // We always update the newest status, so to 'persist' a failure we push a copy to the head of the list - await this.ruleStatusClient.update(currentStatus.id, failureAttributes); - const lastStatus = await this.ruleStatusClient.create(failureAttributes); + await this.ruleStatusClient.update(currentStatus.id, failureAttributes, { references }); + const lastStatus = await this.ruleStatusClient.create(failureAttributes, { references }); // drop oldest failures const oldStatuses = [lastStatus, ...ruleStatuses].slice(MAX_RULE_STATUSES); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/types.ts index e38f974ddee2e..564145cfc5d1f 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_execution_log/types.ts @@ -53,6 +53,7 @@ export interface LogStatusChangeArgs { export interface UpdateExecutionLogArgs { id: string; attributes: IRuleStatusSOAttributes; + ruleId: string; ruleName: string; ruleType: string; spaceId: string; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/delete_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/delete_rules.test.ts index f8e1f873377a9..2d82cd7f8732a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/delete_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/delete_rules.test.ts @@ -26,7 +26,6 @@ describe('deleteRules', () => { type: '', references: [], attributes: { - alertId: 'alertId', statusDate: '', lastFailureAt: null, lastFailureMessage: null, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/enable_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/enable_rule.ts index 2f3d05e0c9586..b75a1b0d80e9a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/enable_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/enable_rule.ts @@ -44,6 +44,7 @@ export const enableRule = async ({ const currentStatusToDisable = ruleCurrentStatus[0]; await ruleStatusClient.update({ id: currentStatusToDisable.id, + ruleId: rule.id, ruleName: rule.name, ruleType: rule.alertTypeId, attributes: { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_prepackaged_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_prepackaged_rules.ts index 6fe326a8d85a3..8116a42f42827 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_prepackaged_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/get_prepackaged_rules.ts @@ -18,7 +18,7 @@ import { // TODO: convert rules files to TS and add explicit type definitions import { rawRules } from './prepackaged_rules'; -import { RuleAssetSavedObjectsClient } from './rule_asset_saved_objects_client'; +import { RuleAssetSavedObjectsClient } from './rule_asset/rule_asset_saved_objects_client'; import { IRuleAssetSOAttributes } from './types'; import { SavedObjectAttributes } from '../../../../../../../src/core/types'; import { ConfigType } from '../../../config'; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/legacy_rule_status/legacy_migrations.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/legacy_rule_status/legacy_migrations.ts new file mode 100644 index 0000000000000..92d7487be0cdb --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/legacy_rule_status/legacy_migrations.ts @@ -0,0 +1,115 @@ +/* + * 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 { + SavedObjectMigrationFn, + SavedObjectReference, + SavedObjectSanitizedDoc, + SavedObjectUnsanitizedDoc, +} from 'kibana/server'; +import { isString } from 'lodash/fp'; +import { truncateMessage } from '../../rule_execution_log'; +import { IRuleSavedAttributesSavedObjectAttributes } from '../types'; +// eslint-disable-next-line no-restricted-imports +import { legacyGetRuleReference } from './legacy_utils'; + +export const truncateMessageFields: SavedObjectMigrationFn> = (doc) => { + const { lastFailureMessage, lastSuccessMessage, ...restAttributes } = doc.attributes; + + return { + ...doc, + attributes: { + lastFailureMessage: truncateMessage(lastFailureMessage), + lastSuccessMessage: truncateMessage(lastSuccessMessage), + ...restAttributes, + }, + references: doc.references ?? [], + }; +}; + +/** + * This side-car rule status SO is deprecated and is to be replaced by the RuleExecutionLog on Event-Log and + * additional fields on the Alerting Framework Rule SO. + * + * @deprecated Remove this once we've fully migrated to event-log and no longer require addition status SO (8.x) + */ +export const legacyRuleStatusSavedObjectMigration = { + '7.15.2': truncateMessageFields, + '7.16.0': ( + doc: SavedObjectUnsanitizedDoc + ): SavedObjectSanitizedDoc => { + return legacyMigrateRuleAlertIdSOReferences(doc); + }, +}; + +/** + * This migrates alertId within legacy `siem-detection-engine-rule-status` to saved object references on an upgrade. + * We only migrate alertId if we find these conditions: + * - alertId is a string and not null, undefined, or malformed data. + * - The existing references do not already have a alertId found within it. + * + * Some of these issues could crop up during either user manual errors of modifying things, earlier migration + * issues, etc... so we are safer to check them as possibilities + * + * @deprecated Remove this once we've fully migrated to event-log and no longer require addition status SO (8.x) + * @param doc The document having an alertId to migrate into references + * @returns The document migrated with saved object references + */ +export const legacyMigrateRuleAlertIdSOReferences = ( + doc: SavedObjectUnsanitizedDoc +): SavedObjectSanitizedDoc => { + const { references } = doc; + + // Isolate alertId from the doc + const { alertId, ...attributesWithoutAlertId } = doc.attributes; + const existingReferences = references ?? []; + + if (!isString(alertId)) { + // early return if alertId is not a string as expected + return { ...doc, references: existingReferences }; + } else { + const alertReferences = legacyMigrateAlertId({ + alertId, + existingReferences, + }); + + return { + ...doc, + attributes: { + ...attributesWithoutAlertId.attributes, + }, + references: [...existingReferences, ...alertReferences], + }; + } +}; + +/** + * This is a helper to migrate "alertId" + * + * @deprecated Remove this once we've fully migrated to event-log and no longer require addition status SO (8.x) + * + * @param existingReferences The existing saved object references + * @param alertId The alertId to migrate + * + * @returns The savedObjectReferences migrated + */ +export const legacyMigrateAlertId = ({ + existingReferences, + alertId, +}: { + existingReferences: SavedObjectReference[]; + alertId: string; +}): SavedObjectReference[] => { + const existingReferenceFound = existingReferences.find((reference) => { + return reference.id === alertId && reference.type === 'alert'; + }); + if (existingReferenceFound) { + return []; + } else { + return [legacyGetRuleReference(alertId)]; + } +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/legacy_rule_status/legacy_rule_status_saved_object_mappings.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/legacy_rule_status/legacy_rule_status_saved_object_mappings.ts new file mode 100644 index 0000000000000..3fe3fc06cc7d6 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/legacy_rule_status/legacy_rule_status_saved_object_mappings.ts @@ -0,0 +1,73 @@ +/* + * 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 { SavedObjectsType } from 'kibana/server'; +// eslint-disable-next-line no-restricted-imports +import { legacyRuleStatusSavedObjectMigration } from './legacy_migrations'; + +/** + * This side-car rule status SO is deprecated and is to be replaced by the RuleExecutionLog on Event-Log and + * additional fields on the Alerting Framework Rule SO. + * + * @deprecated Remove this once we've fully migrated to event-log and no longer require addition status SO (8.x) + */ +export const legacyRuleStatusSavedObjectType = 'siem-detection-engine-rule-status'; + +/** + * This side-car rule status SO is deprecated and is to be replaced by the RuleExecutionLog on Event-Log and + * additional fields on the Alerting Framework Rule SO. + * + * @deprecated Remove this once we've fully migrated to event-log and no longer require addition status SO (8.x) + */ +export const ruleStatusSavedObjectMappings: SavedObjectsType['mappings'] = { + properties: { + status: { + type: 'keyword', + }, + statusDate: { + type: 'date', + }, + lastFailureAt: { + type: 'date', + }, + lastSuccessAt: { + type: 'date', + }, + lastFailureMessage: { + type: 'text', + }, + lastSuccessMessage: { + type: 'text', + }, + lastLookBackDate: { + type: 'date', + }, + gap: { + type: 'text', + }, + bulkCreateTimeDurations: { + type: 'float', + }, + searchAfterTimeDurations: { + type: 'float', + }, + }, +}; + +/** + * This side-car rule status SO is deprecated and is to be replaced by the RuleExecutionLog on Event-Log and + * additional fields on the Alerting Framework Rule SO. + * + * @deprecated Remove this once we've fully migrated to event-log and no longer require addition status SO (8.x) + */ +export const legacyRuleStatusType: SavedObjectsType = { + name: legacyRuleStatusSavedObjectType, + hidden: false, + namespaceType: 'single', + mappings: ruleStatusSavedObjectMappings, + migrations: legacyRuleStatusSavedObjectMigration, +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/legacy_rule_status/legacy_utils.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/legacy_rule_status/legacy_utils.ts new file mode 100644 index 0000000000000..62de5ce591230 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/legacy_rule_status/legacy_utils.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ + +/** + * Given an id this returns a legacy rule reference. + * @param id The id of the alert + * @deprecated Remove this once we've fully migrated to event-log and no longer require addition status SO (8.x) + */ +export const legacyGetRuleReference = (id: string) => ({ + id, + type: 'alert', + name: 'alert_0', +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/rule_asset/rule_asset_saved_object_mappings.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/rule_asset/rule_asset_saved_object_mappings.ts new file mode 100644 index 0000000000000..e2941b503664b --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/rule_asset/rule_asset_saved_object_mappings.ts @@ -0,0 +1,32 @@ +/* + * 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 { SavedObjectsType } from '../../../../../../../../src/core/server'; + +export const ruleAssetSavedObjectType = 'security-rule'; + +export const ruleAssetSavedObjectMappings: SavedObjectsType['mappings'] = { + dynamic: false, + properties: { + name: { + type: 'keyword', + }, + rule_id: { + type: 'keyword', + }, + version: { + type: 'long', + }, + }, +}; + +export const ruleAssetType: SavedObjectsType = { + name: ruleAssetSavedObjectType, + hidden: false, + namespaceType: 'agnostic', + mappings: ruleAssetSavedObjectMappings, +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/rule_asset_saved_objects_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/rule_asset/rule_asset_saved_objects_client.ts similarity index 88% rename from x-pack/plugins/security_solution/server/lib/detection_engine/rules/rule_asset_saved_objects_client.ts rename to x-pack/plugins/security_solution/server/lib/detection_engine/rules/rule_asset/rule_asset_saved_objects_client.ts index ac0969dfc975d..c594385dce22b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/rule_asset_saved_objects_client.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/rule_asset/rule_asset_saved_objects_client.ts @@ -9,9 +9,9 @@ import { SavedObjectsClientContract, SavedObjectsFindOptions, SavedObjectsFindResponse, -} from '../../../../../../../src/core/server'; -import { ruleAssetSavedObjectType } from '../rules/saved_object_mappings'; -import { IRuleAssetSavedObject } from '../rules/types'; +} from 'kibana/server'; +import { ruleAssetSavedObjectType } from './rule_asset_saved_object_mappings'; +import { IRuleAssetSavedObject } from '../types'; const DEFAULT_PAGE_SIZE = 100; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/saved_object_mappings.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/saved_object_mappings.ts deleted file mode 100644 index d347fccf6b77b..0000000000000 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/saved_object_mappings.ts +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { SavedObjectsType, SavedObjectMigrationFn } from 'kibana/server'; -import { truncateMessage } from '../rule_execution_log'; - -export const ruleStatusSavedObjectType = 'siem-detection-engine-rule-status'; - -export const ruleStatusSavedObjectMappings: SavedObjectsType['mappings'] = { - properties: { - alertId: { - type: 'keyword', - }, - status: { - type: 'keyword', - }, - statusDate: { - type: 'date', - }, - lastFailureAt: { - type: 'date', - }, - lastSuccessAt: { - type: 'date', - }, - lastFailureMessage: { - type: 'text', - }, - lastSuccessMessage: { - type: 'text', - }, - lastLookBackDate: { - type: 'date', - }, - gap: { - type: 'text', - }, - bulkCreateTimeDurations: { - type: 'float', - }, - searchAfterTimeDurations: { - type: 'float', - }, - }, -}; - -const truncateMessageFields: SavedObjectMigrationFn> = (doc) => { - const { lastFailureMessage, lastSuccessMessage, ...restAttributes } = doc.attributes; - - return { - ...doc, - attributes: { - lastFailureMessage: truncateMessage(lastFailureMessage), - lastSuccessMessage: truncateMessage(lastSuccessMessage), - ...restAttributes, - }, - references: doc.references ?? [], - }; -}; - -export const type: SavedObjectsType = { - name: ruleStatusSavedObjectType, - hidden: false, - namespaceType: 'single', - mappings: ruleStatusSavedObjectMappings, - migrations: { - '7.15.2': truncateMessageFields, - }, -}; - -export const ruleAssetSavedObjectType = 'security-rule'; - -export const ruleAssetSavedObjectMappings: SavedObjectsType['mappings'] = { - dynamic: false, - properties: { - name: { - type: 'keyword', - }, - rule_id: { - type: 'keyword', - }, - version: { - type: 'long', - }, - }, -}; - -export const ruleAssetType: SavedObjectsType = { - name: ruleAssetSavedObjectType, - hidden: false, - namespaceType: 'agnostic', - mappings: ruleAssetSavedObjectMappings, -}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts index 8adf19a53f92b..53a83d61da78d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/types.ts @@ -111,7 +111,6 @@ export type RuleAlertType = SanitizedAlert; // eslint-disable-next-line @typescript-eslint/no-explicit-any export interface IRuleStatusSOAttributes extends Record { - alertId: string; // created alert id. statusDate: StatusDate; lastFailureAt: LastFailureAt | null | undefined; lastFailureMessage: LastFailureMessage | null | undefined; @@ -125,7 +124,6 @@ export interface IRuleStatusSOAttributes extends Record { } export interface IRuleStatusResponseAttributes { - alert_id: string; // created alert id. status_date: StatusDate; last_failure_at: LastFailureAt | null | undefined; last_failure_message: LastFailureMessage | null | undefined; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts index 207ea497c7e8e..078d36a99ad17 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts @@ -18,7 +18,8 @@ import type { import { SavedObject } from '../../../../../../../../src/core/server'; import { loggingSystemMock } from '../../../../../../../../src/core/server/mocks'; import { IRuleStatusSOAttributes } from '../../rules/types'; -import { ruleStatusSavedObjectType } from '../../rules/saved_object_mappings'; +// eslint-disable-next-line no-restricted-imports +import { legacyRuleStatusSavedObjectType } from '../../rules/legacy_rule_status/legacy_rule_status_saved_object_mappings'; import { getListArrayMock } from '../../../../../common/detection_engine/schemas/types/lists.mock'; import { RulesSchema } from '../../../../../common/detection_engine/schemas/response'; import { RuleParams } from '../../schemas/rule_schemas'; @@ -725,10 +726,9 @@ export const sampleRuleGuid = '04128c15-0d1b-4716-a4c5-46997ac7f3bd'; export const sampleIdGuid = 'e1e08ddc-5e37-49ff-a258-5393aa44435a'; export const exampleRuleStatus: () => SavedObject = () => ({ - type: ruleStatusSavedObjectType, + type: legacyRuleStatusSavedObjectType, id: '042e6d90-7069-11ea-af8b-0f8ae4fa817e', attributes: { - alertId: 'f4b8e31d-cf93-4bde-a265-298bde885cd7', statusDate: '2020-03-27T22:55:59.517Z', status: RuleExecutionStatus.succeeded, lastFailureAt: null, @@ -740,7 +740,13 @@ export const exampleRuleStatus: () => SavedObject = () searchAfterTimeDurations: [], lastLookBackDate: null, }, - references: [], + references: [ + { + id: 'f4b8e31d-cf93-4bde-a265-298bde885cd7', + type: 'alert', + name: 'alert_0', + }, + ], updated_at: '2020-03-27T22:55:59.577Z', version: 'WzgyMiwxXQ==', }); diff --git a/x-pack/plugins/security_solution/server/saved_objects.ts b/x-pack/plugins/security_solution/server/saved_objects.ts index 1523b3ddf4cbf..53618d738984b 100644 --- a/x-pack/plugins/security_solution/server/saved_objects.ts +++ b/x-pack/plugins/security_solution/server/saved_objects.ts @@ -8,10 +8,9 @@ import { CoreSetup } from '../../../../src/core/server'; import { noteType, pinnedEventType, timelineType } from './lib/timeline/saved_object_mappings'; -import { - type as ruleStatusType, - ruleAssetType, -} from './lib/detection_engine/rules/saved_object_mappings'; +// eslint-disable-next-line no-restricted-imports +import { legacyRuleStatusType } from './lib/detection_engine/rules/legacy_rule_status/legacy_rule_status_saved_object_mappings'; +import { ruleAssetType } from './lib/detection_engine/rules/rule_asset/rule_asset_saved_object_mappings'; // eslint-disable-next-line no-restricted-imports import { legacyType as legacyRuleActionsType } from './lib/detection_engine/rule_actions/legacy_saved_object_mappings'; import { type as signalsMigrationType } from './lib/detection_engine/migrations/saved_objects'; @@ -24,7 +23,7 @@ const types = [ noteType, pinnedEventType, legacyRuleActionsType, - ruleStatusType, + legacyRuleStatusType, ruleAssetType, timelineType, exceptionsArtifactType, diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/migrations.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/migrations.ts index 4c0f21df8c0ff..6d1d64a04cd93 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/migrations.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/migrations.ts @@ -86,6 +86,33 @@ export default ({ getService }: FtrProviderContext): void => { '7d' ); }); + + it('migrates legacy siem-detection-engine-rule-status to use saved object references', async () => { + const response = await es.get<{ + 'siem-detection-engine-rule-status': { + alertId: string; + }; + references: [{}]; + }>({ + index: '.kibana', + id: 'siem-detection-engine-rule-status:d62d2980-27c4-11ec-92b0-f7b47106bb35', + }); + expect(response.statusCode).to.eql(200); + + // references exist and are expected values + expect(response.body._source?.references).to.eql([ + { + name: 'alert_0', + id: 'fb1046a0-0452-11ec-9b15-d13d79d162f3', + type: 'alert', + }, + ]); + + // alertId no longer exist + expect(response.body._source?.['siem-detection-engine-rule-status'].alertId).to.eql( + undefined + ); + }); }); }); }; diff --git a/x-pack/test/functional/es_archives/security_solution/migrations/data.json b/x-pack/test/functional/es_archives/security_solution/migrations/data.json index 7b8d81135065d..97a2596f9dba1 100644 --- a/x-pack/test/functional/es_archives/security_solution/migrations/data.json +++ b/x-pack/test/functional/es_archives/security_solution/migrations/data.json @@ -1,4 +1,4 @@ -{ + { "type": "doc", "value": { "id": "siem-detection-engine-rule-actions:fce024a0-0452-11ec-9b15-d13d79d162f3", @@ -29,3 +29,35 @@ } } } + +{ + "type": "doc", + "value": { + "id": "siem-detection-engine-rule-status:d62d2980-27c4-11ec-92b0-f7b47106bb35", + "index": ".kibana_1", + "source": { + "siem-detection-engine-rule-status": { + "alertId": "fb1046a0-0452-11ec-9b15-d13d79d162f3", + "statusDate": "2021-10-11T20:51:26.622Z", + "status": "succeeded", + "lastFailureAt": "2021-10-11T18:10:08.982Z", + "lastSuccessAt": "2021-10-11T20:51:26.622Z", + "lastFailureMessage": "4 days (323690920ms) were not queried between this rule execution and the last execution, so signals may have been missed. Consider increasing your look behind time or adding more Kibana instances. name: \"Threshy\" id: \"fb1046a0-0452-11ec-9b15-d13d79d162f3\" rule id: \"b789c80f-f6d8-41f1-8b4f-b4a23342cde2\" signals index: \".siem-signals-spong-default\"", + "lastSuccessMessage": "succeeded", + "gap": "4 days", + "bulkCreateTimeDurations": [ + "34.49" + ], + "searchAfterTimeDurations": [ + "62.58" + ], + "lastLookBackDate": null + }, + "type": "siem-detection-engine-rule-status", + "references": [], + "coreMigrationVersion": "7.14.0", + "updated_at": "2021-10-11T20:51:26.657Z" + } + } +} + From 0f46bdc5dc144df2e105b36cd09312f1ede437d5 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Mon, 18 Oct 2021 11:40:47 -0400 Subject: [PATCH 2/2] [7.x] Improve upgrade assistance for the legacy audit logger (#114995) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/config_deprecations.test.ts | 259 +++++++++++++----- .../security/server/config_deprecations.ts | 62 ++++- 2 files changed, 238 insertions(+), 83 deletions(-) diff --git a/x-pack/plugins/security/server/config_deprecations.test.ts b/x-pack/plugins/security/server/config_deprecations.test.ts index d4b18a2e1b296..028200d40164c 100644 --- a/x-pack/plugins/security/server/config_deprecations.test.ts +++ b/x-pack/plugins/security/server/config_deprecations.test.ts @@ -16,7 +16,7 @@ const deprecationContext = configDeprecationsMock.createContext(); const applyConfigDeprecations = (settings: Record = {}) => { const deprecations = securityConfigDeprecationProvider(configDeprecationFactory); - const deprecationMessages: string[] = []; + const generatedDeprecations: Array<{ message: string; level?: 'warning' | 'critical' }> = []; const configPaths: string[] = []; const { config: migrated } = applyDeprecations( settings, @@ -26,14 +26,14 @@ const applyConfigDeprecations = (settings: Record = {}) => { context: deprecationContext, })), () => - ({ message, configPath }) => { - deprecationMessages.push(message); + ({ message, level, configPath }) => { + generatedDeprecations.push({ message, level }); configPaths.push(configPath); } ); return { + deprecations: generatedDeprecations, configPaths, - messages: deprecationMessages, migrated, }; }; @@ -41,41 +41,53 @@ const applyConfigDeprecations = (settings: Record = {}) => { describe('Config Deprecations', () => { it('does not report any deprecations if session timeouts are specified', () => { const defaultConfig = { xpack: { security: { session: { idleTimeout: 123, lifespan: 345 } } } }; - const { messages, migrated } = applyConfigDeprecations(cloneDeep(defaultConfig)); + const { deprecations, migrated } = applyConfigDeprecations(cloneDeep(defaultConfig)); expect(migrated).toEqual(defaultConfig); - expect(messages).toHaveLength(0); + expect(deprecations).toHaveLength(0); }); it('reports that session idleTimeout and lifespan will have default values if none of them is specified', () => { const defaultConfig = { xpack: { security: {} } }; - const { messages, migrated } = applyConfigDeprecations(cloneDeep(defaultConfig)); + const { deprecations, migrated } = applyConfigDeprecations(cloneDeep(defaultConfig)); expect(migrated).toEqual(defaultConfig); - expect(messages).toMatchInlineSnapshot(` + expect(deprecations).toMatchInlineSnapshot(` Array [ - "The session idle timeout will default to 1 hour in 8.0.", - "The session lifespan will default to 30 days in 8.0.", + Object { + "level": "warning", + "message": "The session idle timeout will default to 1 hour in 8.0.", + }, + Object { + "level": "warning", + "message": "The session lifespan will default to 30 days in 8.0.", + }, ] `); }); it('reports that session idleTimeout will have a default value if it is not specified', () => { const defaultConfig = { xpack: { security: { session: { lifespan: 345 } } } }; - const { messages, migrated } = applyConfigDeprecations(cloneDeep(defaultConfig)); + const { deprecations, migrated } = applyConfigDeprecations(cloneDeep(defaultConfig)); expect(migrated).toEqual(defaultConfig); - expect(messages).toMatchInlineSnapshot(` + expect(deprecations).toMatchInlineSnapshot(` Array [ - "The session idle timeout will default to 1 hour in 8.0.", + Object { + "level": "warning", + "message": "The session idle timeout will default to 1 hour in 8.0.", + }, ] `); }); it('reports that session lifespan will have a default value if it is not specified', () => { const defaultConfig = { xpack: { security: { session: { idleTimeout: 123 } } } }; - const { messages, migrated } = applyConfigDeprecations(cloneDeep(defaultConfig)); + const { deprecations, migrated } = applyConfigDeprecations(cloneDeep(defaultConfig)); expect(migrated).toEqual(defaultConfig); - expect(messages).toMatchInlineSnapshot(` + expect(deprecations).toMatchInlineSnapshot(` Array [ - "The session lifespan will default to 30 days in 8.0.", + Object { + "level": "warning", + "message": "The session lifespan will default to 30 days in 8.0.", + }, ] `); }); @@ -88,13 +100,19 @@ describe('Config Deprecations', () => { }, }, }; - const { messages, migrated } = applyConfigDeprecations(cloneDeep(config)); + const { deprecations, migrated } = applyConfigDeprecations(cloneDeep(config)); expect(migrated.xpack.security.sessionTimeout).not.toBeDefined(); expect(migrated.xpack.security.session.idleTimeout).toEqual(123); - expect(messages).toMatchInlineSnapshot(` + expect(deprecations).toMatchInlineSnapshot(` Array [ - "Setting \\"xpack.security.sessionTimeout\\" has been replaced by \\"xpack.security.session.idleTimeout\\"", - "The session lifespan will default to 30 days in 8.0.", + Object { + "level": undefined, + "message": "Setting \\"xpack.security.sessionTimeout\\" has been replaced by \\"xpack.security.session.idleTimeout\\"", + }, + Object { + "level": "warning", + "message": "The session lifespan will default to 30 days in 8.0.", + }, ] `); }); @@ -112,12 +130,15 @@ describe('Config Deprecations', () => { }, }, }; - const { messages, migrated } = applyConfigDeprecations(cloneDeep(config)); + const { deprecations, migrated } = applyConfigDeprecations(cloneDeep(config)); expect(migrated.xpack.security.audit.appender.kind).not.toBeDefined(); expect(migrated.xpack.security.audit.appender.type).toEqual('console'); - expect(messages).toMatchInlineSnapshot(` + expect(deprecations).toMatchInlineSnapshot(` Array [ - "Setting \\"xpack.security.audit.appender.kind\\" has been replaced by \\"xpack.security.audit.appender.type\\"", + Object { + "level": undefined, + "message": "Setting \\"xpack.security.audit.appender.kind\\" has been replaced by \\"xpack.security.audit.appender.type\\"", + }, ] `); }); @@ -135,12 +156,15 @@ describe('Config Deprecations', () => { }, }, }; - const { messages, migrated } = applyConfigDeprecations(cloneDeep(config)); + const { deprecations, migrated } = applyConfigDeprecations(cloneDeep(config)); expect(migrated.xpack.security.audit.appender.layout.kind).not.toBeDefined(); expect(migrated.xpack.security.audit.appender.layout.type).toEqual('pattern'); - expect(messages).toMatchInlineSnapshot(` + expect(deprecations).toMatchInlineSnapshot(` Array [ - "Setting \\"xpack.security.audit.appender.layout.kind\\" has been replaced by \\"xpack.security.audit.appender.layout.type\\"", + Object { + "level": undefined, + "message": "Setting \\"xpack.security.audit.appender.layout.kind\\" has been replaced by \\"xpack.security.audit.appender.layout.type\\"", + }, ] `); }); @@ -158,12 +182,15 @@ describe('Config Deprecations', () => { }, }, }; - const { messages, migrated } = applyConfigDeprecations(cloneDeep(config)); + const { deprecations, migrated } = applyConfigDeprecations(cloneDeep(config)); expect(migrated.xpack.security.audit.appender.policy.kind).not.toBeDefined(); expect(migrated.xpack.security.audit.appender.policy.type).toEqual('time-interval'); - expect(messages).toMatchInlineSnapshot(` + expect(deprecations).toMatchInlineSnapshot(` Array [ - "Setting \\"xpack.security.audit.appender.policy.kind\\" has been replaced by \\"xpack.security.audit.appender.policy.type\\"", + Object { + "level": undefined, + "message": "Setting \\"xpack.security.audit.appender.policy.kind\\" has been replaced by \\"xpack.security.audit.appender.policy.type\\"", + }, ] `); }); @@ -181,12 +208,15 @@ describe('Config Deprecations', () => { }, }, }; - const { messages, migrated } = applyConfigDeprecations(cloneDeep(config)); + const { deprecations, migrated } = applyConfigDeprecations(cloneDeep(config)); expect(migrated.xpack.security.audit.appender.strategy.kind).not.toBeDefined(); expect(migrated.xpack.security.audit.appender.strategy.type).toEqual('numeric'); - expect(messages).toMatchInlineSnapshot(` + expect(deprecations).toMatchInlineSnapshot(` Array [ - "Setting \\"xpack.security.audit.appender.strategy.kind\\" has been replaced by \\"xpack.security.audit.appender.strategy.type\\"", + Object { + "level": undefined, + "message": "Setting \\"xpack.security.audit.appender.strategy.kind\\" has been replaced by \\"xpack.security.audit.appender.strategy.type\\"", + }, ] `); }); @@ -205,12 +235,15 @@ describe('Config Deprecations', () => { }, }, }; - const { messages, migrated } = applyConfigDeprecations(cloneDeep(config)); + const { deprecations, migrated } = applyConfigDeprecations(cloneDeep(config)); expect(migrated.xpack.security.audit.appender.path).not.toBeDefined(); expect(migrated.xpack.security.audit.appender.fileName).toEqual('./audit.log'); - expect(messages).toMatchInlineSnapshot(` + expect(deprecations).toMatchInlineSnapshot(` Array [ - "Setting \\"xpack.security.audit.appender.path\\" has been replaced by \\"xpack.security.audit.appender.fileName\\"", + Object { + "level": undefined, + "message": "Setting \\"xpack.security.audit.appender.path\\" has been replaced by \\"xpack.security.audit.appender.fileName\\"", + }, ] `); }); @@ -226,39 +259,94 @@ describe('Config Deprecations', () => { showInsecureClusterWarning: false, }, }; - const { messages, migrated } = applyConfigDeprecations(cloneDeep(config)); + const { deprecations, migrated } = applyConfigDeprecations(cloneDeep(config)); expect(migrated.security.showInsecureClusterWarning).not.toBeDefined(); expect(migrated.xpack.security.showInsecureClusterWarning).toEqual(false); - expect(messages).toMatchInlineSnapshot(` + expect(deprecations).toMatchInlineSnapshot(` Array [ - "Setting \\"security.showInsecureClusterWarning\\" has been replaced by \\"xpack.security.showInsecureClusterWarning\\"", + Object { + "level": undefined, + "message": "Setting \\"security.showInsecureClusterWarning\\" has been replaced by \\"xpack.security.showInsecureClusterWarning\\"", + }, + ] + `); + }); + + it('warns when using the legacy audit logger on-prem', () => { + const config = { + xpack: { + security: { + session: { idleTimeout: 123, lifespan: 345 }, + audit: { + enabled: true, + }, + }, + }, + }; + const { deprecations, migrated } = applyConfigDeprecations(cloneDeep(config)); + expect(migrated.xpack.security.audit.appender).not.toBeDefined(); + expect(deprecations).toMatchInlineSnapshot(` + Array [ + Object { + "level": "warning", + "message": "Use the new ECS-compliant audit logger.", + }, ] `); }); - it('warns when using the legacy audit logger', () => { + it('does not warn when using the ECS audit logger on-prem', () => { const config = { xpack: { security: { session: { idleTimeout: 123, lifespan: 345 }, audit: { enabled: true, + appender: { + type: 'file', + fileName: './audit.log', + }, }, }, }, }; - const { messages, migrated } = applyConfigDeprecations(cloneDeep(config)); + const { deprecations, migrated } = applyConfigDeprecations(cloneDeep(config)); + expect(migrated).toEqual(config); + expect(deprecations).toHaveLength(0); + }); + + it('warns when using the legacy audit logger on cloud', () => { + const config = { + xpack: { + cloud: { + id: 'abc123', + }, + security: { + session: { idleTimeout: 123, lifespan: 345 }, + audit: { + enabled: true, + }, + }, + }, + }; + const { deprecations, migrated } = applyConfigDeprecations(cloneDeep(config)); expect(migrated.xpack.security.audit.appender).not.toBeDefined(); - expect(messages).toMatchInlineSnapshot(` + expect(deprecations).toMatchInlineSnapshot(` Array [ - "The legacy audit logger is deprecated in favor of the new ECS-compliant audit logger.", + Object { + "level": "warning", + "message": "Use the new ECS-compliant audit logger.", + }, ] `); }); - it('does not warn when using the ECS audit logger', () => { + it('does not warn when using the ECS audit logger on cloud', () => { const config = { xpack: { + cloud: { + id: 'abc123', + }, security: { session: { idleTimeout: 123, lifespan: 345 }, audit: { @@ -271,9 +359,9 @@ describe('Config Deprecations', () => { }, }, }; - const { messages, migrated } = applyConfigDeprecations(cloneDeep(config)); + const { deprecations, migrated } = applyConfigDeprecations(cloneDeep(config)); expect(migrated).toEqual(config); - expect(messages).toHaveLength(0); + expect(deprecations).toHaveLength(0); }); it('does not warn about using the legacy logger when using the ECS audit logger, even when using the deprecated ECS appender config', () => { @@ -291,12 +379,15 @@ describe('Config Deprecations', () => { }, }, }; - const { messages, migrated } = applyConfigDeprecations(cloneDeep(config)); + const { deprecations, migrated } = applyConfigDeprecations(cloneDeep(config)); expect(migrated.xpack.security.audit.appender.path).not.toBeDefined(); expect(migrated.xpack.security.audit.appender.fileName).toEqual('./audit.log'); - expect(messages).toMatchInlineSnapshot(` + expect(deprecations).toMatchInlineSnapshot(` Array [ - "Setting \\"xpack.security.audit.appender.path\\" has been replaced by \\"xpack.security.audit.appender.fileName\\"", + Object { + "level": undefined, + "message": "Setting \\"xpack.security.audit.appender.path\\" has been replaced by \\"xpack.security.audit.appender.fileName\\"", + }, ] `); }); @@ -314,10 +405,13 @@ describe('Config Deprecations', () => { }, }, }; - const { messages } = applyConfigDeprecations(cloneDeep(config)); - expect(messages).toMatchInlineSnapshot(` + const { deprecations } = applyConfigDeprecations(cloneDeep(config)); + expect(deprecations).toMatchInlineSnapshot(` Array [ - "You no longer need to configure \\"xpack.security.authorization.legacyFallback.enabled\\".", + Object { + "level": undefined, + "message": "You no longer need to configure \\"xpack.security.authorization.legacyFallback.enabled\\".", + }, ] `); }); @@ -335,10 +429,13 @@ describe('Config Deprecations', () => { }, }, }; - const { messages } = applyConfigDeprecations(cloneDeep(config)); - expect(messages).toMatchInlineSnapshot(` + const { deprecations } = applyConfigDeprecations(cloneDeep(config)); + expect(deprecations).toMatchInlineSnapshot(` Array [ - "You no longer need to configure \\"xpack.security.authc.saml.maxRedirectURLSize\\".", + Object { + "level": undefined, + "message": "You no longer need to configure \\"xpack.security.authc.saml.maxRedirectURLSize\\".", + }, ] `); }); @@ -360,10 +457,13 @@ describe('Config Deprecations', () => { }, }, }; - const { messages, configPaths } = applyConfigDeprecations(cloneDeep(config)); - expect(messages).toMatchInlineSnapshot(` + const { deprecations, configPaths } = applyConfigDeprecations(cloneDeep(config)); + expect(deprecations).toMatchInlineSnapshot(` Array [ - "\\"xpack.security.authc.providers.saml..maxRedirectURLSize\\" is no longer used.", + Object { + "level": undefined, + "message": "\\"xpack.security.authc.providers.saml..maxRedirectURLSize\\" is no longer used.", + }, ] `); @@ -381,11 +481,14 @@ describe('Config Deprecations', () => { }, }, }; - const { messages, migrated } = applyConfigDeprecations(cloneDeep(config)); + const { deprecations, migrated } = applyConfigDeprecations(cloneDeep(config)); expect(migrated).toEqual(config); - expect(messages).toMatchInlineSnapshot(` + expect(deprecations).toMatchInlineSnapshot(` Array [ - "\\"xpack.security.authc.providers\\" accepts an extended \\"object\\" format instead of an array of provider types.", + Object { + "level": undefined, + "message": "\\"xpack.security.authc.providers\\" accepts an extended \\"object\\" format instead of an array of provider types.", + }, ] `); }); @@ -401,12 +504,18 @@ describe('Config Deprecations', () => { }, }, }; - const { messages, migrated } = applyConfigDeprecations(cloneDeep(config)); + const { deprecations, migrated } = applyConfigDeprecations(cloneDeep(config)); expect(migrated).toEqual(config); - expect(messages).toMatchInlineSnapshot(` + expect(deprecations).toMatchInlineSnapshot(` Array [ - "\\"xpack.security.authc.providers\\" accepts an extended \\"object\\" format instead of an array of provider types.", - "Enabling both \\"basic\\" and \\"token\\" authentication providers in \\"xpack.security.authc.providers\\" is deprecated. Login page will only use \\"token\\" provider.", + Object { + "level": undefined, + "message": "\\"xpack.security.authc.providers\\" accepts an extended \\"object\\" format instead of an array of provider types.", + }, + Object { + "level": undefined, + "message": "Enabling both \\"basic\\" and \\"token\\" authentication providers in \\"xpack.security.authc.providers\\" is deprecated. Login page will only use \\"token\\" provider.", + }, ] `); }); @@ -420,11 +529,14 @@ describe('Config Deprecations', () => { }, }, }; - const { messages, migrated } = applyConfigDeprecations(cloneDeep(config)); + const { deprecations, migrated } = applyConfigDeprecations(cloneDeep(config)); expect(migrated).toEqual(config); - expect(messages).toMatchInlineSnapshot(` + expect(deprecations).toMatchInlineSnapshot(` Array [ - "Enabling or disabling the Security plugin in Kibana is deprecated. Configure security in Elasticsearch instead.", + Object { + "level": undefined, + "message": "Enabling or disabling the Security plugin in Kibana is deprecated. Configure security in Elasticsearch instead.", + }, ] `); }); @@ -438,11 +550,14 @@ describe('Config Deprecations', () => { }, }, }; - const { messages, migrated } = applyConfigDeprecations(cloneDeep(config)); + const { deprecations, migrated } = applyConfigDeprecations(cloneDeep(config)); expect(migrated).toEqual(config); - expect(messages).toMatchInlineSnapshot(` + expect(deprecations).toMatchInlineSnapshot(` Array [ - "Enabling or disabling the Security plugin in Kibana is deprecated. Configure security in Elasticsearch instead.", + Object { + "level": undefined, + "message": "Enabling or disabling the Security plugin in Kibana is deprecated. Configure security in Elasticsearch instead.", + }, ] `); }); @@ -455,8 +570,8 @@ describe('Config Deprecations', () => { }, }, }; - const { messages, migrated } = applyConfigDeprecations(cloneDeep(config)); + const { deprecations, migrated } = applyConfigDeprecations(cloneDeep(config)); expect(migrated).toEqual(config); - expect(messages).toHaveLength(0); + expect(deprecations).toHaveLength(0); }); }); diff --git a/x-pack/plugins/security/server/config_deprecations.ts b/x-pack/plugins/security/server/config_deprecations.ts index c8c8e64648c4b..e080fc1217e94 100644 --- a/x-pack/plugins/security/server/config_deprecations.ts +++ b/x-pack/plugins/security/server/config_deprecations.ts @@ -33,22 +33,62 @@ export const securityConfigDeprecationProvider: ConfigDeprecationProvider = ({ (settings, fromPath, addDeprecation, { branch }) => { const auditLoggingEnabled = settings?.xpack?.security?.audit?.enabled ?? false; const legacyAuditLoggerEnabled = !settings?.xpack?.security?.audit?.appender; - if (auditLoggingEnabled && legacyAuditLoggerEnabled) { + + // Gross, but the cloud plugin depends on the security plugin already, + // so we can't add a dependency in the other direction to check this in a more conventional manner. + const isCloudInstance = typeof settings?.xpack?.cloud?.id === 'string'; + + const isUsingLegacyAuditLogger = auditLoggingEnabled && legacyAuditLoggerEnabled; + + if (!isUsingLegacyAuditLogger) { + return; + } + + const title = i18n.translate('xpack.security.deprecations.auditLoggerTitle', { + defaultMessage: 'The legacy audit logger is deprecated', + }); + + const message = i18n.translate('xpack.security.deprecations.auditLoggerMessage', { + defaultMessage: 'Use the new ECS-compliant audit logger.', + }); + + const documentationUrl = `https://www.elastic.co/guide/en/kibana/${branch}/security-settings-kb.html#audit-logging-settings`; + + const configPath = 'xpack.security.audit.appender'; + if (isCloudInstance) { addDeprecation({ - configPath: 'xpack.security.audit.appender', - title: i18n.translate('xpack.security.deprecations.auditLoggerTitle', { - defaultMessage: 'The legacy audit logger is deprecated', - }), - message: i18n.translate('xpack.security.deprecations.auditLoggerMessage', { - defaultMessage: - 'The legacy audit logger is deprecated in favor of the new ECS-compliant audit logger.', - }), - documentationUrl: `https://www.elastic.co/guide/en/kibana/${branch}/security-settings-kb.html#audit-logging-settings`, + title, + message, + configPath, + level: 'warning', + documentationUrl, + correctiveActions: { + manualSteps: [ + i18n.translate('xpack.security.deprecations.auditLogger.manualStepOneMessageCloud', { + defaultMessage: + 'To enable the ECS audit logger now, add the "xpack.security.audit.appender.type: rolling-file" setting.', + }), + i18n.translate('xpack.security.deprecations.auditLogger.manualStepTwoMessageCloud', { + defaultMessage: `If you don't make any changes, the ECS audit logger will be enabled when you upgrade to 8.0.`, + }), + ], + }, + }); + } else { + addDeprecation({ + title, + message, + configPath, + level: 'warning', + documentationUrl, correctiveActions: { manualSteps: [ i18n.translate('xpack.security.deprecations.auditLogger.manualStepOneMessage', { defaultMessage: - 'Declare an audit logger "appender" via "xpack.security.audit.appender" to enable the ECS audit logger.', + 'To enable the ECS audit logger now, configure an appender with "xpack.security.audit.appender".', + }), + i18n.translate('xpack.security.deprecations.auditLogger.manualStepTwoMessage', { + defaultMessage: `If you don't make any changes, the ECS audit logger will be enabled when you upgrade to 8.0.`, }), ], },