From 2f74c4e3e5cb4a820268369d2e0a023ff3aa3a99 Mon Sep 17 00:00:00 2001 From: Devin Hurley Date: Mon, 18 Oct 2021 11:44:04 -0400 Subject: [PATCH] update patchRules function with same logic as updateRules for migrating actions --- .../rules/add_prepackaged_rules_route.ts | 1 + .../routes/rules/import_rules_route.ts | 1 + .../routes/rules/patch_rules_bulk_route.ts | 1 + .../routes/rules/patch_rules_route.ts | 1 + .../rules/patch_rules.mock.ts | 3 ++ .../lib/detection_engine/rules/patch_rules.ts | 53 +++++++++++++++++-- .../lib/detection_engine/rules/types.ts | 1 + .../rules/update_prepacked_rules.test.ts | 4 ++ .../rules/update_prepacked_rules.ts | 5 ++ 9 files changed, 67 insertions(+), 3 deletions(-) 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 fed34743e220a..ddf4e956beac4 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 @@ -171,6 +171,7 @@ export const createPrepackagedRules = async ( ); await updatePrepackagedRules( rulesClient, + savedObjectsClient, context.securitySolution.getSpaceId(), ruleStatusClient, rulesToUpdate, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts index 8269fe8b36132..9a0623cb83820 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/import_rules_route.ts @@ -273,6 +273,7 @@ export const importRulesRoute = ( } else if (rule != null && request.query.overwrite) { await patchRules({ rulesClient, + savedObjectsClient, author, buildingBlockType, spaceId: context.securitySolution.getSpaceId(), diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts index 67d68221d846f..3fa576d89d16b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_bulk_route.ts @@ -136,6 +136,7 @@ export const patchRulesBulkRoute = ( const rule = await patchRules({ rule: existingRule, rulesClient, + savedObjectsClient, author, buildingBlockType, description, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts index cf140f22289de..bbec98e935406 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/patch_rules_route.ts @@ -136,6 +136,7 @@ export const patchRulesRoute = ( const rule = await patchRules({ rulesClient, + savedObjectsClient, author, buildingBlockType, description, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts index 1d09e4ca5c508..3626bcd5f127e 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.mock.ts @@ -7,6 +7,7 @@ import { PatchRulesOptions } from './types'; import { rulesClientMock } from '../../../../../alerting/server/mocks'; +import { savedObjectsClientMock } from '../../../../../../../src/core/server/mocks'; import { getAlertMock } from '../routes/__mocks__/request_responses'; import { getMlRuleParams, getQueryRuleParams } from '../schemas/rule_schemas.mock'; import { ruleExecutionLogClientMock } from '../rule_execution_log/__mocks__/rule_execution_log_client'; @@ -15,6 +16,7 @@ export const getPatchRulesOptionsMock = (isRuleRegistryEnabled: boolean): PatchR author: ['Elastic'], buildingBlockType: undefined, rulesClient: rulesClientMock.create(), + savedObjectsClient: savedObjectsClientMock.create(), spaceId: 'default', ruleStatusClient: ruleExecutionLogClientMock.create(), anomalyThreshold: undefined, @@ -68,6 +70,7 @@ export const getPatchMlRulesOptionsMock = (isRuleRegistryEnabled: boolean): Patc author: ['Elastic'], buildingBlockType: undefined, rulesClient: rulesClientMock.create(), + savedObjectsClient: savedObjectsClientMock.create(), spaceId: 'default', ruleStatusClient: ruleExecutionLogClientMock.create(), anomalyThreshold: 55, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts index c3b7e7288dc57..70b35ec019fee 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/patch_rules.ts @@ -13,6 +13,8 @@ import { normalizeMachineLearningJobIds, normalizeThresholdObject, } from '../../../../common/detection_engine/utils'; +// eslint-disable-next-line no-restricted-imports +import { legacyRuleActionsSavedObjectType } from '../rule_actions/legacy_saved_object_mappings'; import { internalRuleUpdate, RuleParams } from '../schemas/rule_schemas'; import { addTags } from './add_tags'; import { enableRule } from './enable_rule'; @@ -35,8 +37,10 @@ class PatchError extends Error { } } +// eslint-disable-next-line complexity export const patchRules = async ({ rulesClient, + savedObjectsClient, author, buildingBlockType, ruleStatusClient, @@ -92,6 +96,39 @@ export const patchRules = async ({ return null; } + /** + * On update / patch I'm going to take the actions as they are, better off taking rules client.find (siem.notification) result + * and putting that into the actions array of the rule, then set the rules onThrottle property, notifyWhen and throttle from null -> actualy value (1hr etc..) + * Then use the rules client to delete the siem.notification + * Then with the legacy Rule Actions saved object type, just delete it. + */ + + // find it using the references array, not params.ruleAlertId + let migratedRule = false; + const siemNotification = await rulesClient.find({ + options: { + hasReference: { + type: 'alert', + id: rule.id, + }, + }, + }); + + const legacyRuleActionsSO = await savedObjectsClient.find({ + type: legacyRuleActionsSavedObjectType, + }); + + if (siemNotification != null && siemNotification.data.length > 0) { + await rulesClient.delete({ id: siemNotification.data[0].id }); + if (legacyRuleActionsSO != null && legacyRuleActionsSO.saved_objects.length > 0) { + await savedObjectsClient.delete( + legacyRuleActionsSavedObjectType, + legacyRuleActionsSO.saved_objects[0].id + ); + } + migratedRule = true; + } + const calculatedVersion = calculateVersion(rule.params.immutable, rule.params.version, { author, buildingBlockType, @@ -191,14 +228,24 @@ export const patchRules = async ({ const newRule = { tags: addTags(tags ?? rule.tags, rule.params.ruleId, rule.params.immutable), - throttle: throttle !== undefined ? transformToAlertThrottle(throttle) : rule.throttle, - notifyWhen: throttle !== undefined ? transformToNotifyWhen(throttle) : rule.notifyWhen, name: calculateName({ updatedName: name, originalName: rule.name }), schedule: { interval: calculateInterval(interval, rule.schedule.interval), }, - actions: actions?.map(transformRuleToAlertAction) ?? rule.actions, params: removeUndefined(nextParams), + actions: migratedRule + ? siemNotification.data[0].actions + : actions?.map(transformRuleToAlertAction) ?? rule.actions, + throttle: migratedRule + ? siemNotification.data[0].schedule.interval + : throttle !== undefined + ? transformToAlertThrottle(throttle) + : rule.throttle, + notifyWhen: migratedRule + ? transformToNotifyWhen(siemNotification.data[0].throttle) + : throttle !== undefined + ? transformToNotifyWhen(throttle) + : rule.notifyWhen, }; const [validated, errors] = validate(newRule, internalRuleUpdate); 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 0b43d678633e9..969188f823dc3 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 @@ -283,6 +283,7 @@ export interface PatchRulesOptions { spaceId: string; ruleStatusClient: IRuleExecutionLogClient; rulesClient: RulesClient; + savedObjectsClient: SavedObjectsClientContract; anomalyThreshold: AnomalyThresholdOrUndefined; author: AuthorOrUndefined; buildingBlockType: BuildingBlockTypeOrUndefined; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.test.ts index 7c9f0c9ec67a3..9bd0fe3cef59a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.test.ts @@ -6,6 +6,7 @@ */ import { rulesClientMock } from '../../../../../alerting/server/mocks'; +import { savedObjectsClientMock } from '../../../../../../../src/core/server/mocks'; import { getFindResultWithSingleHit } from '../routes/__mocks__/request_responses'; import { updatePrepackagedRules } from './update_prepacked_rules'; import { patchRules } from './patch_rules'; @@ -19,10 +20,12 @@ describe.each([ ])('updatePrepackagedRules - %s', (_, isRuleRegistryEnabled) => { let rulesClient: ReturnType; let ruleStatusClient: ReturnType; + let savedObjectsClient: ReturnType; beforeEach(() => { rulesClient = rulesClientMock.create(); ruleStatusClient = ruleExecutionLogClientMock.create(); + savedObjectsClient = savedObjectsClientMock.create(); }); it('should omit actions and enabled when calling patchRules', async () => { @@ -40,6 +43,7 @@ describe.each([ await updatePrepackagedRules( rulesClient, + savedObjectsClient, 'default', ruleStatusClient, [{ ...prepackagedRule, actions }], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.ts index d9c2ecd1b5732..00f48b0bd1932 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/update_prepacked_rules.ts @@ -6,6 +6,7 @@ */ import { chunk } from 'lodash/fp'; +import { SavedObjectsClientContract } from 'kibana/server'; import { AddPrepackagedRulesSchemaDecoded } from '../../../../common/detection_engine/schemas/request/add_prepackaged_rules_schema'; import { RulesClient, PartialAlert } from '../../../../../alerting/server'; import { patchRules } from './patch_rules'; @@ -51,6 +52,7 @@ export const UPDATE_CHUNK_SIZE = 50; */ export const updatePrepackagedRules = async ( rulesClient: RulesClient, + savedObjectsClient: SavedObjectsClientContract, spaceId: string, ruleStatusClient: IRuleExecutionLogClient, rules: AddPrepackagedRulesSchemaDecoded[], @@ -61,6 +63,7 @@ export const updatePrepackagedRules = async ( for (const ruleChunk of ruleChunks) { const rulePromises = createPromises( rulesClient, + savedObjectsClient, spaceId, ruleStatusClient, ruleChunk, @@ -82,6 +85,7 @@ export const updatePrepackagedRules = async ( */ export const createPromises = ( rulesClient: RulesClient, + savedObjectsClient: SavedObjectsClientContract, spaceId: string, ruleStatusClient: IRuleExecutionLogClient, rules: AddPrepackagedRulesSchemaDecoded[], @@ -150,6 +154,7 @@ export const createPromises = ( // or enable rules on the user when they were not expecting it if a rule updates return patchRules({ rulesClient, + savedObjectsClient, author, buildingBlockType, description,