From 00257565b78812c0a6919bd724189278a907e3b8 Mon Sep 17 00:00:00 2001 From: Maxim Palenov Date: Wed, 3 May 2023 18:18:15 +0200 Subject: [PATCH 1/5] add a migration to unmute custom security solution rules --- .../saved_objects/migrations/8.8/index.ts | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations/8.8/index.ts b/x-pack/plugins/alerting/server/saved_objects/migrations/8.8/index.ts index 5e3c32cca378d..acfbb21cb0a1a 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations/8.8/index.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations/8.8/index.ts @@ -73,9 +73,30 @@ function addSecuritySolutionActionsFrequency( return doc; } +function unmuteSecuritySolutionCustomRules( + doc: SavedObjectUnsanitizedDoc +): SavedObjectUnsanitizedDoc { + if (!isDetectionEngineAADRuleType(doc) || doc.attributes.params.immutable) { + return doc; + } + + return { + ...doc, + attributes: { + ...doc.attributes, + muteAll: false, + }, + }; +} + export const getMigrations880 = (encryptedSavedObjects: EncryptedSavedObjectsPluginSetup) => createEsoMigration( encryptedSavedObjects, (doc: SavedObjectUnsanitizedDoc): doc is SavedObjectUnsanitizedDoc => true, - pipeMigrations(addActionUuid, addRevision, addSecuritySolutionActionsFrequency) + pipeMigrations( + addActionUuid, + addRevision, + addSecuritySolutionActionsFrequency, + unmuteSecuritySolutionCustomRules + ) ); From e40ef2fa1de11c2904848f51bfd8401a021789d8 Mon Sep 17 00:00:00 2001 From: Maxim Palenov Date: Thu, 4 May 2023 13:06:42 +0200 Subject: [PATCH 2/5] add tests --- .../saved_objects/migrations/index.test.ts | 73 ++++++++++++++++--- .../tests/alerting/group4/migrations.ts | 21 ++++++ .../functional/es_archives/alerts/data.json | 67 ++++++++++++++++- 3 files changed, 148 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations/index.test.ts b/x-pack/plugins/alerting/server/saved_objects/migrations/index.test.ts index 1a250ecd0914b..9ce9f6bb834e0 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations/index.test.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations/index.test.ts @@ -2653,20 +2653,26 @@ describe('successful migrations', () => { ]); }); - test('migrates rule to include revision and defaults revision to 0', () => { - const migration880 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['8.8.0']; + describe('security rule version to revision', () => { + test('migrates rule to include revision and defaults revision to 0', () => { + const migration880 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '8.8.0' + ]; - const rule = getMockData(); - const migratedAlert880 = migration880(rule, migrationContext); - expect(migratedAlert880.attributes.revision).toEqual(0); - }); + const rule = getMockData(); + const migratedAlert880 = migration880(rule, migrationContext); + expect(migratedAlert880.attributes.revision).toEqual(0); + }); - test('migrates security rule version to revision', () => { - const migration880 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)['8.8.0']; + test('migrates security rule version to revision', () => { + const migration880 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '8.8.0' + ]; - const rule = getMockData({ alertTypeId: ruleTypeMappings.eql, params: { version: 2 } }); - const migratedAlert880 = migration880(rule, migrationContext); - expect(migratedAlert880.attributes.revision).toEqual(2); + const rule = getMockData({ alertTypeId: ruleTypeMappings.eql, params: { version: 2 } }); + const migratedAlert880 = migration880(rule, migrationContext); + expect(migratedAlert880.attributes.revision).toEqual(2); + }); }); describe('migrate actions frequency for Security Solution ', () => { @@ -2714,6 +2720,51 @@ describe('successful migrations', () => { expect(updatedActions).toEqual(rule.attributes.actions); }); }); + + describe('unmute security rules', () => { + test.each(Object.values(ruleTypeMappings))( + 'unmutes custom rules of type "%s" successfully', + (ruleType) => { + const migration880 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '8.8.0' + ]; + + const rule = getMockData({ alertTypeId: ruleType, muteAll: true }); + const migratedAlert880 = migration880(rule, migrationContext); + + expect(migratedAlert880.attributes.muteAll).toBeFalsy(); + } + ); + + test('ignores prebuilt rules', () => { + const migration880 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '8.8.0' + ]; + + const rule = getMockData({ + alertTypeId: ruleTypeMappings.query, + muteAll: true, + params: { immutable: true }, + }); + const migratedAlert880 = migration880(rule, migrationContext); + + expect(migratedAlert880.attributes.muteAll).toBeTruthy(); + }); + + test('ignores non security rules', () => { + const migration880 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ + '8.8.0' + ]; + + const rule = getMockData({ + alertTypeId: 'unknown', + muteAll: true, + }); + const migratedAlert880 = migration880(rule, migrationContext); + + expect(migratedAlert880.attributes.muteAll).toBeTruthy(); + }); + }); }); describe('Metrics Inventory Threshold rule', () => { diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations.ts index 2b936a17ce04b..2401d1ece419f 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations.ts @@ -692,5 +692,26 @@ export default function createGetTests({ getService }: FtrProviderContext) { }), ]); }); + + it('8.8 unmutes only security custom rules', async () => { + const nonSecurityRuleId = 'alert:74f3e6d7-b7bb-477d-ac28-92ee22728e6e'; + const securityImmutableRuleId = 'alert:8990af61-c09a-11ec-9164-4bfd6fc32c43'; + const securityCustomRuleId = 'alert:88bc8c21-07ba-42eb-ad9c-06820275ac10'; + + const { docs } = await es.mget({ + index: ALERTING_CASES_SAVED_OBJECT_INDEX, + body: { ids: [securityCustomRuleId, nonSecurityRuleId, securityImmutableRuleId] }, + }); + + expect( + (docs[0] as estypes.GetGetResult<{ alert: RawRule }>)?._source?.alert.muteAll + ).toBeFalsy(); + expect( + (docs[1] as estypes.GetGetResult<{ alert: RawRule }>)?._source?.alert.muteAll + ).toBeTruthy(); + expect( + (docs[2] as estypes.GetGetResult<{ alert: RawRule }>)?._source?.alert.muteAll + ).toBeTruthy(); + }); }); } diff --git a/x-pack/test/functional/es_archives/alerts/data.json b/x-pack/test/functional/es_archives/alerts/data.json index 868379350df2a..87081086064b9 100644 --- a/x-pack/test/functional/es_archives/alerts/data.json +++ b/x-pack/test/functional/es_archives/alerts/data.json @@ -14,7 +14,7 @@ "createdAt": "2020-06-17T15:35:38.497Z", "createdBy": "elastic", "enabled": true, - "muteAll": false, + "muteAll": true, "mutedInstanceIds": [ ], "name": "always-firing-alert", @@ -1023,7 +1023,7 @@ "createdBy":"elastic", "updatedBy":"elastic", "createdAt":"2021-07-27T20:42:55.896Z", - "muteAll":false, + "muteAll":true, "mutedInstanceIds":[ ], @@ -1407,4 +1407,67 @@ } } } +} + +{ + "type":"doc", + "value":{ + "id":"alert:88bc8c21-07ba-42eb-ad9c-06820275ac10", + "index":".kibana_1", + "source":{ + "alert":{ + "name":"Test unmuting of a custom security rule", + "alertTypeId":"siem.queryRule", + "consumer":"siem", + "params":{ + "immutable":false, + "ruleId":"bf9638eb-8d3c-4f40-83d7-8c40a7c80f2e", + "author":[], + "description":"Test unmuting of a custom security rule", + "falsePositives":[], + "from":"now-36000060s", + "license":"", + "outputIndex":".siem-signals-default", + "meta":{ + "from":"10000h" + }, + "maxSignals":100, + "riskScore":21, + "riskScoreMapping":[], + "severity":"low", + "severityMapping":[], + "threat":[], + "to":"now", + "references":[], + "version":0, + "exceptionsList":[], + "type":"query", + "language":"kuery", + "index":["test-index"], + "query":"*:*", + "filters":[] + }, + "schedule":{ + "interval":"5m" + }, + "enabled":false, + "actions": [], + "apiKeyOwner":null, + "apiKey":null, + "createdBy":"elastic", + "updatedBy":"elastic", + "createdAt":"2023-03-27T20:42:55.896Z", + "muteAll":true, + "mutedInstanceIds":[], + "scheduledTaskId":null, + "tags":[] + }, + "type":"alert", + "migrationVersion":{ + "alert":"8.7.0" + }, + "updated_at":"2023-03-27T20:42:55.896Z", + "references":[] + } + } } \ No newline at end of file From 57b002baf3593f198d9c14d98b49d1c4322d8bf1 Mon Sep 17 00:00:00 2001 From: Maxim Palenov Date: Thu, 4 May 2023 13:12:35 +0200 Subject: [PATCH 3/5] add an explaining comment --- .../alerting/server/saved_objects/migrations/8.8/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations/8.8/index.ts b/x-pack/plugins/alerting/server/saved_objects/migrations/8.8/index.ts index acfbb21cb0a1a..27a0ca3956ab3 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations/8.8/index.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations/8.8/index.ts @@ -76,6 +76,7 @@ function addSecuritySolutionActionsFrequency( function unmuteSecuritySolutionCustomRules( doc: SavedObjectUnsanitizedDoc ): SavedObjectUnsanitizedDoc { + // "doc.attributes.params.immutable" is set to "true" for prebuilt rules if (!isDetectionEngineAADRuleType(doc) || doc.attributes.params.immutable) { return doc; } From a4c9472ba07e61180e6359ff020c0fcfa53b1954 Mon Sep 17 00:00:00 2001 From: Maxim Palenov Date: Thu, 4 May 2023 14:12:18 +0200 Subject: [PATCH 4/5] update the comment --- .../alerting/server/saved_objects/migrations/8.8/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations/8.8/index.ts b/x-pack/plugins/alerting/server/saved_objects/migrations/8.8/index.ts index 27a0ca3956ab3..9dff18a1fecef 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations/8.8/index.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations/8.8/index.ts @@ -76,6 +76,8 @@ function addSecuritySolutionActionsFrequency( function unmuteSecuritySolutionCustomRules( doc: SavedObjectUnsanitizedDoc ): SavedObjectUnsanitizedDoc { + // only security custom rules were muted if there is no actions prior 8.8 + // there is no reason to unmute prebuilt rules // "doc.attributes.params.immutable" is set to "true" for prebuilt rules if (!isDetectionEngineAADRuleType(doc) || doc.attributes.params.immutable) { return doc; From 5c55432858eb753570f9dc7ed2226908e49b01b9 Mon Sep 17 00:00:00 2001 From: Maxim Palenov Date: Thu, 4 May 2023 18:56:19 +0200 Subject: [PATCH 5/5] apply unmute migration to all security rules --- .../saved_objects/migrations/8.8/index.ts | 5 +--- .../saved_objects/migrations/index.test.ts | 15 ---------- .../tests/alerting/group4/migrations.ts | 29 ++++++++++--------- 3 files changed, 16 insertions(+), 33 deletions(-) diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations/8.8/index.ts b/x-pack/plugins/alerting/server/saved_objects/migrations/8.8/index.ts index 9dff18a1fecef..24629a3a146bd 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations/8.8/index.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations/8.8/index.ts @@ -76,10 +76,7 @@ function addSecuritySolutionActionsFrequency( function unmuteSecuritySolutionCustomRules( doc: SavedObjectUnsanitizedDoc ): SavedObjectUnsanitizedDoc { - // only security custom rules were muted if there is no actions prior 8.8 - // there is no reason to unmute prebuilt rules - // "doc.attributes.params.immutable" is set to "true" for prebuilt rules - if (!isDetectionEngineAADRuleType(doc) || doc.attributes.params.immutable) { + if (!isDetectionEngineAADRuleType(doc)) { return doc; } diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations/index.test.ts b/x-pack/plugins/alerting/server/saved_objects/migrations/index.test.ts index 9ce9f6bb834e0..28a807986b39b 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations/index.test.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations/index.test.ts @@ -2736,21 +2736,6 @@ describe('successful migrations', () => { } ); - test('ignores prebuilt rules', () => { - const migration880 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ - '8.8.0' - ]; - - const rule = getMockData({ - alertTypeId: ruleTypeMappings.query, - muteAll: true, - params: { immutable: true }, - }); - const migratedAlert880 = migration880(rule, migrationContext); - - expect(migratedAlert880.attributes.muteAll).toBeTruthy(); - }); - test('ignores non security rules', () => { const migration880 = getMigrations(encryptedSavedObjectsSetup, {}, isPreconfigured)[ '8.8.0' diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations.ts index 2401d1ece419f..5611341bd3223 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/group4/migrations.ts @@ -693,25 +693,26 @@ export default function createGetTests({ getService }: FtrProviderContext) { ]); }); - it('8.8 unmutes only security custom rules', async () => { - const nonSecurityRuleId = 'alert:74f3e6d7-b7bb-477d-ac28-92ee22728e6e'; - const securityImmutableRuleId = 'alert:8990af61-c09a-11ec-9164-4bfd6fc32c43'; + it('8.8 unmutes only security rules', async () => { const securityCustomRuleId = 'alert:88bc8c21-07ba-42eb-ad9c-06820275ac10'; + const securityImmutableRuleId = 'alert:8990af61-c09a-11ec-9164-4bfd6fc32c43'; + const nonSecurityRuleId = 'alert:74f3e6d7-b7bb-477d-ac28-92ee22728e6e'; - const { docs } = await es.mget({ + const { docs } = await es.mget<{ alert: RawRule }>({ index: ALERTING_CASES_SAVED_OBJECT_INDEX, - body: { ids: [securityCustomRuleId, nonSecurityRuleId, securityImmutableRuleId] }, + body: { ids: [securityCustomRuleId, securityImmutableRuleId, nonSecurityRuleId] }, }); - expect( - (docs[0] as estypes.GetGetResult<{ alert: RawRule }>)?._source?.alert.muteAll - ).toBeFalsy(); - expect( - (docs[1] as estypes.GetGetResult<{ alert: RawRule }>)?._source?.alert.muteAll - ).toBeTruthy(); - expect( - (docs[2] as estypes.GetGetResult<{ alert: RawRule }>)?._source?.alert.muteAll - ).toBeTruthy(); + const securityCustomRuleMuteAll = + '_source' in docs[0] ? docs[0]._source?.alert.muteAll : undefined; + const securityImmutableRuleMuteAll = + '_source' in docs[1] ? docs[1]._source?.alert.muteAll : undefined; + const nonSecurityRuleMuteAll = + '_source' in docs[2] ? docs[2]._source?.alert.muteAll : undefined; + + expect(securityCustomRuleMuteAll).toBeFalsy(); + expect(securityImmutableRuleMuteAll).toBeFalsy(); + expect(nonSecurityRuleMuteAll).toBeTruthy(); }); }); }