From 60cf299af8cb44e3f87ac4f88c3927973ebad1fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20=C3=81brah=C3=A1m?= Date: Fri, 17 May 2024 18:05:41 +0200 Subject: [PATCH] [EDR Workflows] Add new backfill function to set `on_write_scan` to `false` if Malware protection is off (#182598) ## Summary This PR adds an additional backfill for `on_write_scan`. This is a fix, because it has been backfilled with a `true` value, but it should be `false` when Malware protection is off. This should be a safe backfill, because: - the feature is behind feature flag, so users did not have the opportunity to modify the value, - and if they would have, still, `malware=off && on_write_scan=true` is an invalid combination, the user cannot achieve it, so there is no chance of destroying user created settings. Additionally, the new model version is added to a different folder to help transitioning our mindsets from migrations to model versions. ### Checklist Delete any items that are not applicable to this PR. - [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 --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../check_registered_types.test.ts | 2 +- .../fleet/server/saved_objects/index.ts | 9 + .../model_versions/security_solution/index.ts | 8 + .../v10_on_write_scan_fix.test.ts | 183 ++++++++++++++++++ .../v10_on_write_scan_fix.ts | 42 ++++ 5 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/index.ts create mode 100644 x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/v10_on_write_scan_fix.test.ts create mode 100644 x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/v10_on_write_scan_fix.ts diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index 6b8a29c70227b..015c517e9a6b8 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -112,7 +112,7 @@ describe('checking migration metadata changes on all registered SO types', () => "ingest-agent-policies": "803dc27e106440c41e8f3c3d8ee8bbb0821bcde2", "ingest-download-sources": "279a68147e62e4d8858c09ad1cf03bd5551ce58d", "ingest-outputs": "daafff49255ab700e07491376fe89f04fc998b91", - "ingest-package-policies": "d63e091b2b3cf2eecaa46ae2533bdd5214a983fc", + "ingest-package-policies": "e6da7d0ee2996241ade23b3a7811fe5d3e449cb2", "ingest_manager_settings": "91445219e7115ff0c45d1dabd5d614a80b421797", "inventory-view": "b8683c8e352a286b4aca1ab21003115a4800af83", "kql-telemetry": "93c1d16c1a0dfca9c8842062cf5ef8f62ae401ad", diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index 22b5aa3f5fddb..f74366d924c96 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -85,6 +85,7 @@ import { migratePackagePolicyEvictionsFromV81102, } from './migrations/security_solution/to_v8_11_0_2'; import { settingsV1 } from './model_versions/v1'; +import { packagePolicyV10OnWriteScanFix } from './model_versions/security_solution'; /* * Saved object types and mappings @@ -540,6 +541,14 @@ export const getSavedObjectTypes = ( }, ], }, + '10': { + changes: [ + { + type: 'data_backfill', + backfillFn: packagePolicyV10OnWriteScanFix, + }, + ], + }, }, migrations: { '7.10.0': migratePackagePolicyToV7100, diff --git a/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/index.ts b/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/index.ts new file mode 100644 index 0000000000000..780bc55f9cd3e --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { packagePolicyV10OnWriteScanFix } from './v10_on_write_scan_fix'; diff --git a/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/v10_on_write_scan_fix.test.ts b/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/v10_on_write_scan_fix.test.ts new file mode 100644 index 0000000000000..37ca3dd6d4b0d --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/v10_on_write_scan_fix.test.ts @@ -0,0 +1,183 @@ +/* + * 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 type { SavedObject } from '@kbn/core-saved-objects-api-server'; +import type { ModelVersionTestMigrator } from '@kbn/core-test-helpers-model-versions'; +import { createModelVersionTestMigrator } from '@kbn/core-test-helpers-model-versions'; + +import { getSavedObjectTypes } from '../..'; + +import type { PackagePolicy } from '../../../../common'; +import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../../../common'; + +describe('backfill for modelVersion 10 - fix on_write_scan field', () => { + let migrator: ModelVersionTestMigrator; + let policyConfigSO: SavedObject; + + beforeEach(() => { + migrator = createModelVersionTestMigrator({ + type: getSavedObjectTypes()[PACKAGE_POLICY_SAVED_OBJECT_TYPE], + }); + + policyConfigSO = { + id: 'mock-saved-object-id', + attributes: { + name: 'Some Policy Name', + package: { + name: 'endpoint', + title: '', + version: '', + }, + id: 'endpoint', + policy_id: '', + enabled: true, + namespace: '', + revision: 0, + updated_at: '', + updated_by: '', + created_at: '', + created_by: '', + inputs: [ + { + type: 'endpoint', + enabled: true, + streams: [], + config: { + policy: { + value: { + windows: { + malware: { + mode: 'detect', + }, + antivirus_registration: { + enabled: true, + }, + }, + mac: { + malware: { + mode: 'detect', + }, + }, + linux: { + malware: { + mode: 'detect', + }, + }, + }, + }, + }, + }, + ], + }, + type: PACKAGE_POLICY_SAVED_OBJECT_TYPE, + references: [], + }; + }); + + describe('when updating to model version 10', () => { + it('should change `on_write_scan` from `true` to `false` if Malware is off', () => { + setMalwareMode(policyConfigSO, 'off'); + setOnWriteScan(policyConfigSO, true); + + const migratedPolicyConfigSO = migrator.migrate({ + document: policyConfigSO, + fromVersion: 9, + toVersion: 10, + }); + + expectOnWriteScanToBe(false, migratedPolicyConfigSO); + }); + + it('should not change `on_write_scan` if Malware is detect', () => { + setMalwareMode(policyConfigSO, 'detect'); + setOnWriteScan(policyConfigSO, true); + + const migratedPolicyConfigSO = migrator.migrate({ + document: policyConfigSO, + fromVersion: 9, + toVersion: 10, + }); + + expectOnWriteScanToBe(true, migratedPolicyConfigSO); + }); + + it('should not change `on_write_scan` if Malware is prevent', () => { + setMalwareMode(policyConfigSO, 'prevent'); + setOnWriteScan(policyConfigSO, true); + + const migratedPolicyConfigSO = migrator.migrate({ + document: policyConfigSO, + fromVersion: 9, + toVersion: 10, + }); + + expectOnWriteScanToBe(true, migratedPolicyConfigSO); + }); + }); + + describe('additional test: when updating from model version 5 to model version 10', () => { + it('should add `on_write_scan=false` if Malware is off', () => { + setMalwareMode(policyConfigSO, 'off'); + + const migratedPolicyConfigSO = migrator.migrate({ + document: policyConfigSO, + fromVersion: 5, + toVersion: 10, + }); + + expectOnWriteScanToBe(false, migratedPolicyConfigSO); + }); + + it('should add `on_write_scan=true` if Malware is detect', () => { + setMalwareMode(policyConfigSO, 'detect'); + + const migratedPolicyConfigSO = migrator.migrate({ + document: policyConfigSO, + fromVersion: 5, + toVersion: 10, + }); + + expectOnWriteScanToBe(true, migratedPolicyConfigSO); + }); + + it('should add `on_write_scan=true` if Malware is prevent', () => { + setMalwareMode(policyConfigSO, 'prevent'); + + const migratedPolicyConfigSO = migrator.migrate({ + document: policyConfigSO, + fromVersion: 5, + toVersion: 10, + }); + + expectOnWriteScanToBe(true, migratedPolicyConfigSO); + }); + }); + + const setMalwareMode = (so: SavedObject, level: 'off' | 'detect' | 'prevent') => { + const config = so.attributes.inputs[0].config?.policy.value; + + config.windows.malware.mode = level; + config.mac.malware.mode = level; + config.linux.malware.mode = level; + }; + + const setOnWriteScan = (so: SavedObject, value: boolean) => { + const config = so.attributes.inputs[0].config?.policy.value; + + config.windows.malware.on_write_scan = value; + config.mac.malware.on_write_scan = value; + config.linux.malware.on_write_scan = value; + }; + + const expectOnWriteScanToBe = (expectedValue: boolean, so: SavedObject) => { + const config = so.attributes.inputs[0].config?.policy.value; + + expect(config.windows.malware.on_write_scan).toBe(expectedValue); + expect(config.mac.malware.on_write_scan).toBe(expectedValue); + expect(config.linux.malware.on_write_scan).toBe(expectedValue); + }; +}); diff --git a/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/v10_on_write_scan_fix.ts b/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/v10_on_write_scan_fix.ts new file mode 100644 index 0000000000000..7f793f7980164 --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/model_versions/security_solution/v10_on_write_scan_fix.ts @@ -0,0 +1,42 @@ +/* + * 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 type { + SavedObjectModelDataBackfillFn, + SavedObjectUnsanitizedDoc, +} from '@kbn/core-saved-objects-server'; + +import type { PackagePolicy } from '../../../../common'; + +export const packagePolicyV10OnWriteScanFix: SavedObjectModelDataBackfillFn< + PackagePolicy, + PackagePolicy +> = (packagePolicyDoc) => { + if (packagePolicyDoc.attributes.package?.name !== 'endpoint') { + return { attributes: packagePolicyDoc.attributes }; + } + + const updatedPackagePolicyDoc: SavedObjectUnsanitizedDoc = packagePolicyDoc; + + const input = updatedPackagePolicyDoc.attributes.inputs[0]; + + if (input && input.config) { + const policy = input.config.policy.value; + + if (policy.windows.malware.mode === 'off') { + policy.windows.malware.on_write_scan = false; + } + if (policy.mac.malware.mode === 'off') { + policy.mac.malware.on_write_scan = false; + } + if (policy.linux.malware.mode === 'off') { + policy.linux.malware.on_write_scan = false; + } + } + + return { attributes: updatedPackagePolicyDoc.attributes }; +};