From ebc02741b3ec5e0caaa6a811d5aa066d030202a0 Mon Sep 17 00:00:00 2001 From: Esteban Beltran Date: Mon, 19 Jul 2021 18:42:11 +0200 Subject: [PATCH] [Security Solution]Memory protection configuration card for policies integration. (#101365) --- .../fleet/server/saved_objects/index.ts | 2 + .../migrations/security_solution/index.ts | 1 + .../security_solution/to_v7_15_0.test.ts | 174 +++++++++++++++++ .../security_solution/to_v7_15_0.ts | 43 +++++ .../saved_objects/migrations/to_v7_15_0.ts | 26 +++ .../common/endpoint/models/policy_config.ts | 22 ++- .../common/endpoint/types/index.ts | 13 +- .../common/license/policy_config.test.ts | 180 ++++++++++++++---- .../common/license/policy_config.ts | 44 ++++- .../policy/store/policy_details/index.test.ts | 5 + .../policy/store/policy_details/middleware.ts | 15 +- .../policy/store/policy_details/selectors.ts | 1 + .../public/management/pages/policy/types.ts | 8 +- .../pages/policy/view/policy_details.test.tsx | 21 +- .../pages/policy/view/policy_details_form.tsx | 24 ++- .../policy/view/policy_forms/locked_card.tsx | 13 +- .../view/policy_forms/protections/memory.tsx | 60 ++++++ .../protections/popup_options_to_versions.ts | 1 + .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - .../apps/endpoint/policy_details.ts | 15 ++ 21 files changed, 611 insertions(+), 59 deletions(-) create mode 100644 x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/to_v7_15_0.test.ts create mode 100644 x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/to_v7_15_0.ts create mode 100644 x-pack/plugins/fleet/server/saved_objects/migrations/to_v7_15_0.ts create mode 100644 x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/memory.tsx diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index fe8771115a2174..9f9f0dab6efacd 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -43,6 +43,7 @@ import { migrateOutputToV7130, } from './migrations/to_v7_13_0'; import { migratePackagePolicyToV7140 } from './migrations/to_v7_14_0'; +import { migratePackagePolicyToV7150 } from './migrations/to_v7_15_0'; /* * Saved object types and mappings @@ -272,6 +273,7 @@ const getSavedObjectTypes = ( '7.12.0': migratePackagePolicyToV7120, '7.13.0': migratePackagePolicyToV7130, '7.14.0': migratePackagePolicyToV7140, + '7.15.0': migratePackagePolicyToV7150, }, }, [PACKAGES_SAVED_OBJECT_TYPE]: { diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/index.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/index.ts index b4f09e541298a2..e7945077999831 100644 --- a/x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/index.ts @@ -9,3 +9,4 @@ export { migratePackagePolicyToV7110 } from './to_v7_11_0'; export { migratePackagePolicyToV7120 } from './to_v7_12_0'; export { migrateEndpointPackagePolicyToV7130 } from './to_v7_13_0'; export { migrateEndpointPackagePolicyToV7140 } from './to_v7_14_0'; +export { migratePackagePolicyToV7150 } from './to_v7_15_0'; diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/to_v7_15_0.test.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/to_v7_15_0.test.ts new file mode 100644 index 00000000000000..ac0ba62673d606 --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/to_v7_15_0.test.ts @@ -0,0 +1,174 @@ +/* + * 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 { SavedObjectMigrationContext, SavedObjectUnsanitizedDoc } from 'kibana/server'; + +import type { PackagePolicy } from '../../../../common'; + +import { migratePackagePolicyToV7150 as migration } from './to_v7_15_0'; + +describe('7.15.0 Endpoint Package Policy migration', () => { + const policyDoc = ({ + windowsMemory = {}, + windowsPopup = {}, + windowsMalware = {}, + windowsRansomware = {}, + }) => { + return { + id: 'mock-saved-object-id', + attributes: { + name: 'Some Policy Name', + package: { + name: 'endpoint', + title: '', + version: '', + }, + id: 'endpoint', + policy_id: '', + enabled: true, + namespace: '', + output_id: '', + revision: 0, + updated_at: '', + updated_by: '', + created_at: '', + created_by: '', + inputs: [ + { + type: 'endpoint', + enabled: true, + streams: [], + config: { + policy: { + value: { + windows: { + ...windowsMalware, + ...windowsRansomware, + ...windowsMemory, + ...windowsPopup, + }, + }, + }, + }, + }, + ], + }, + type: ' nested', + }; + }; + + it('adds windows memory protection alongside malware and ramsomware', () => { + const initialDoc = policyDoc({ + windowsMalware: { malware: { mode: 'off' } }, + windowsRansomware: { ransomware: { mode: 'off', supported: true } }, + windowsPopup: { + popup: { + malware: { + message: '', + enabled: true, + }, + ransomware: { + message: '', + enabled: true, + }, + }, + }, + }); + + const migratedDoc = policyDoc({ + windowsMalware: { malware: { mode: 'off' } }, + windowsRansomware: { ransomware: { mode: 'off', supported: true } }, + // new memory protection + windowsMemory: { memory_protection: { mode: 'off', supported: true } }, + windowsPopup: { + popup: { + malware: { + message: '', + enabled: true, + }, + ransomware: { + message: '', + enabled: true, + }, + // new memory popup setup + memory_protection: { + message: '', + enabled: false, + }, + }, + }, + }); + + expect(migration(initialDoc, {} as SavedObjectMigrationContext)).toEqual(migratedDoc); + }); + + it('does not modify non-endpoint package policies', () => { + const doc: SavedObjectUnsanitizedDoc = { + id: 'mock-saved-object-id', + attributes: { + name: 'Some Policy Name', + package: { + name: 'notEndpoint', + title: '', + version: '', + }, + id: 'notEndpoint', + policy_id: '', + enabled: true, + namespace: '', + output_id: '', + revision: 0, + updated_at: '', + updated_by: '', + created_at: '', + created_by: '', + inputs: [ + { + type: 'notEndpoint', + enabled: true, + streams: [], + config: {}, + }, + ], + }, + type: ' nested', + }; + + expect( + migration(doc, {} as SavedObjectMigrationContext) as SavedObjectUnsanitizedDoc + ).toEqual({ + attributes: { + name: 'Some Policy Name', + package: { + name: 'notEndpoint', + title: '', + version: '', + }, + id: 'notEndpoint', + policy_id: '', + enabled: true, + namespace: '', + output_id: '', + revision: 0, + updated_at: '', + updated_by: '', + created_at: '', + created_by: '', + inputs: [ + { + type: 'notEndpoint', + enabled: true, + streams: [], + config: {}, + }, + ], + }, + type: ' nested', + id: 'mock-saved-object-id', + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/to_v7_15_0.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/to_v7_15_0.ts new file mode 100644 index 00000000000000..e820cb47132a69 --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/security_solution/to_v7_15_0.ts @@ -0,0 +1,43 @@ +/* + * 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 { SavedObjectMigrationFn, SavedObjectUnsanitizedDoc } from 'kibana/server'; +import { cloneDeep } from 'lodash'; + +import type { PackagePolicy } from '../../../../common'; + +export const migratePackagePolicyToV7150: SavedObjectMigrationFn = ( + packagePolicyDoc +) => { + if (packagePolicyDoc.attributes.package?.name !== 'endpoint') { + return packagePolicyDoc; + } + + const updatedPackagePolicyDoc: SavedObjectUnsanitizedDoc = cloneDeep( + packagePolicyDoc + ); + + const input = updatedPackagePolicyDoc.attributes.inputs[0]; + const memory = { + mode: 'off', + // This value is based on license. + // For the migration, we add 'true', our license watcher will correct it, if needed, when the app starts. + supported: true, + }; + const memoryPopup = { + message: '', + enabled: false, + }; + if (input && input.config) { + const policy = input.config.policy.value; + + policy.windows.memory_protection = memory; + policy.windows.popup.memory_protection = memoryPopup; + } + + return updatedPackagePolicyDoc; +}; diff --git a/x-pack/plugins/fleet/server/saved_objects/migrations/to_v7_15_0.ts b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v7_15_0.ts new file mode 100644 index 00000000000000..27758296d8d95e --- /dev/null +++ b/x-pack/plugins/fleet/server/saved_objects/migrations/to_v7_15_0.ts @@ -0,0 +1,26 @@ +/* + * 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 { SavedObjectMigrationFn } from 'kibana/server'; + +import type { PackagePolicy } from '../../../common'; + +import { migratePackagePolicyToV7150 as SecSolMigratePackagePolicyToV7150 } from './security_solution'; + +export const migratePackagePolicyToV7150: SavedObjectMigrationFn = ( + packagePolicyDoc, + migrationContext +) => { + let updatedPackagePolicyDoc = packagePolicyDoc; + + // Endpoint specific migrations + if (packagePolicyDoc.attributes.package?.name === 'endpoint') { + updatedPackagePolicyDoc = SecSolMigratePackagePolicyToV7150(packagePolicyDoc, migrationContext); + } + + return updatedPackagePolicyDoc; +}; diff --git a/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts b/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts index 63784b8b8b4403..72aae68000e989 100644 --- a/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts +++ b/x-pack/plugins/security_solution/common/endpoint/models/policy_config.ts @@ -29,6 +29,10 @@ export const policyFactory = (): PolicyConfig => { mode: ProtectionModes.prevent, supported: true, }, + memory_protection: { + mode: ProtectionModes.prevent, + supported: true, + }, popup: { malware: { message: '', @@ -38,6 +42,10 @@ export const policyFactory = (): PolicyConfig => { message: '', enabled: true, }, + memory_protection: { + message: '', + enabled: true, + }, }, logging: { file: 'info', @@ -101,6 +109,10 @@ export const policyFactoryWithoutPaidFeatures = ( mode: ProtectionModes.off, supported: false, }, + memory_protection: { + mode: ProtectionModes.off, + supported: false, + }, popup: { ...policy.windows.popup, malware: { @@ -111,6 +123,10 @@ export const policyFactoryWithoutPaidFeatures = ( message: '', enabled: false, }, + memory_protection: { + message: '', + enabled: false, + }, }, }, mac: { @@ -150,6 +166,10 @@ export const policyFactoryWithSupportedFeatures = ( ...policy.windows.ransomware, supported: true, }, + memory_protection: { + ...policy.windows.memory_protection, + supported: true, + }, }, }; }; @@ -157,4 +177,4 @@ export const policyFactoryWithSupportedFeatures = ( /** * Reflects what string the Endpoint will use when message field is default/empty */ -export const DefaultMalwareMessage = 'Elastic Security {action} {filename}'; +export const DefaultPolicyNotificationMessage = 'Elastic Security {action} {filename}'; diff --git a/x-pack/plugins/security_solution/common/endpoint/types/index.ts b/x-pack/plugins/security_solution/common/endpoint/types/index.ts index 076eb51a5fdc54..fd119ba2e4a98c 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/index.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/index.ts @@ -864,6 +864,7 @@ export interface PolicyConfig { security: boolean; }; malware: ProtectionFields; + memory_protection: ProtectionFields & SupportedFields; ransomware: ProtectionFields & SupportedFields; logging: { file: string; @@ -877,6 +878,10 @@ export interface PolicyConfig { message: string; enabled: boolean; }; + memory_protection: { + message: string; + enabled: boolean; + }; }; antivirus_registration: { enabled: boolean; @@ -929,7 +934,13 @@ export interface UIPolicyConfig { */ windows: Pick< PolicyConfig['windows'], - 'events' | 'malware' | 'ransomware' | 'popup' | 'antivirus_registration' | 'advanced' + | 'events' + | 'malware' + | 'ransomware' + | 'popup' + | 'antivirus_registration' + | 'advanced' + | 'memory_protection' >; /** * Mac-specific policy configuration that is supported via the UI diff --git a/x-pack/plugins/security_solution/common/license/policy_config.test.ts b/x-pack/plugins/security_solution/common/license/policy_config.test.ts index 219538184765a4..71668d97e89442 100644 --- a/x-pack/plugins/security_solution/common/license/policy_config.test.ts +++ b/x-pack/plugins/security_solution/common/license/policy_config.test.ts @@ -10,7 +10,7 @@ import { unsetPolicyFeaturesAccordingToLicenseLevel, } from './policy_config'; import { - DefaultMalwareMessage, + DefaultPolicyNotificationMessage, policyFactory, policyFactoryWithSupportedFeatures, policyFactoryWithoutPaidFeatures, @@ -75,55 +75,102 @@ describe('policy_config and licenses', () => { expect(valid).toBeFalsy(); }); - it('allows ransomware to be turned on for Platinum licenses', () => { + it('allows ransomware and memory to be turned on for Platinum licenses', () => { const policy = policyFactoryWithoutPaidFeatures(); policy.windows.ransomware.mode = ProtectionModes.prevent; policy.windows.ransomware.supported = true; + policy.windows.memory_protection.mode = ProtectionModes.prevent; + policy.windows.memory_protection.supported = true; const valid = isEndpointPolicyValidForLicense(policy, Platinum); expect(valid).toBeTruthy(); }); - it('blocks ransomware to be turned on for Gold and below licenses', () => { - const policy = policyFactoryWithoutPaidFeatures(); - policy.windows.ransomware.mode = ProtectionModes.prevent; - - let valid = isEndpointPolicyValidForLicense(policy, Gold); - expect(valid).toBeFalsy(); - valid = isEndpointPolicyValidForLicense(policy, Basic); - expect(valid).toBeFalsy(); - }); - it('allows ransomware notification to be turned on with a Platinum license', () => { + it('allows ransomware and memory_protection notification to be turned on with a Platinum license', () => { const policy = policyFactoryWithoutPaidFeatures(); policy.windows.popup.ransomware.enabled = true; policy.windows.ransomware.supported = true; + policy.windows.popup.memory_protection.enabled = true; + policy.windows.memory_protection.supported = true; const valid = isEndpointPolicyValidForLicense(policy, Platinum); expect(valid).toBeTruthy(); }); - it('blocks ransomware notification to be turned on for Gold and below licenses', () => { - const policy = policyFactoryWithoutPaidFeatures(); - policy.windows.popup.ransomware.enabled = true; - let valid = isEndpointPolicyValidForLicense(policy, Gold); - expect(valid).toBeFalsy(); - valid = isEndpointPolicyValidForLicense(policy, Basic); - expect(valid).toBeFalsy(); + describe('ransomware protection checks', () => { + it('blocks ransomware to be turned on for Gold and below licenses', () => { + const policy = policyFactoryWithoutPaidFeatures(); + policy.windows.ransomware.mode = ProtectionModes.prevent; + + let valid = isEndpointPolicyValidForLicense(policy, Gold); + expect(valid).toBeFalsy(); + valid = isEndpointPolicyValidForLicense(policy, Basic); + expect(valid).toBeFalsy(); + }); + + it('blocks ransomware notification to be turned on for Gold and below licenses', () => { + const policy = policyFactoryWithoutPaidFeatures(); + policy.windows.popup.ransomware.enabled = true; + let valid = isEndpointPolicyValidForLicense(policy, Gold); + expect(valid).toBeFalsy(); + + valid = isEndpointPolicyValidForLicense(policy, Basic); + expect(valid).toBeFalsy(); + }); + + it('allows ransomware notification message changes with a Platinum license', () => { + const policy = policyFactory(); + policy.windows.popup.ransomware.message = 'BOOM'; + const valid = isEndpointPolicyValidForLicense(policy, Platinum); + expect(valid).toBeTruthy(); + }); + it('blocks ransomware notification message changes for Gold and below licenses', () => { + const policy = policyFactory(); + policy.windows.popup.ransomware.message = 'BOOM'; + let valid = isEndpointPolicyValidForLicense(policy, Gold); + expect(valid).toBeFalsy(); + + valid = isEndpointPolicyValidForLicense(policy, Basic); + expect(valid).toBeFalsy(); + }); }); - it('allows ransomware notification message changes with a Platinum license', () => { - const policy = policyFactory(); - policy.windows.popup.ransomware.message = 'BOOM'; - const valid = isEndpointPolicyValidForLicense(policy, Platinum); - expect(valid).toBeTruthy(); - }); - it('blocks ransomware notification message changes for Gold and below licenses', () => { - const policy = policyFactory(); - policy.windows.popup.ransomware.message = 'BOOM'; - let valid = isEndpointPolicyValidForLicense(policy, Gold); - expect(valid).toBeFalsy(); - - valid = isEndpointPolicyValidForLicense(policy, Basic); - expect(valid).toBeFalsy(); + describe('memory protection checks', () => { + it('blocks memory_protection to be turned on for Gold and below licenses', () => { + const policy = policyFactoryWithoutPaidFeatures(); + policy.windows.memory_protection.mode = ProtectionModes.prevent; + + let valid = isEndpointPolicyValidForLicense(policy, Gold); + expect(valid).toBeFalsy(); + valid = isEndpointPolicyValidForLicense(policy, Basic); + expect(valid).toBeFalsy(); + }); + + it('blocks memory_protection notification to be turned on for Gold and below licenses', () => { + const policy = policyFactoryWithoutPaidFeatures(); + policy.windows.popup.memory_protection.enabled = true; + let valid = isEndpointPolicyValidForLicense(policy, Gold); + expect(valid).toBeFalsy(); + + valid = isEndpointPolicyValidForLicense(policy, Basic); + expect(valid).toBeFalsy(); + }); + + it('allows memory_protection notification message changes with a Platinum license', () => { + const policy = policyFactory(); + policy.windows.popup.memory_protection.message = 'BOOM'; + const valid = isEndpointPolicyValidForLicense(policy, Platinum); + expect(valid).toBeTruthy(); + }); + + it('blocks memory_protection notification message changes for Gold and below licenses', () => { + const policy = policyFactory(); + policy.windows.popup.memory_protection.message = 'BOOM'; + let valid = isEndpointPolicyValidForLicense(policy, Gold); + expect(valid).toBeFalsy(); + + valid = isEndpointPolicyValidForLicense(policy, Basic); + expect(valid).toBeFalsy(); + }); }); it('allows default policyConfig with Basic', () => { @@ -160,6 +207,19 @@ describe('policy_config and licenses', () => { expect(retPolicy.windows.popup.ransomware.message).toEqual(popupMessage); }); + it('does not change any memory fields with a Platinum license', () => { + const policy = policyFactory(); + const popupMessage = 'WOOP WOOP'; + policy.windows.memory_protection.mode = ProtectionModes.detect; + policy.windows.popup.memory_protection.enabled = false; + policy.windows.popup.memory_protection.message = popupMessage; + + const retPolicy = unsetPolicyFeaturesAccordingToLicenseLevel(policy, Platinum); + expect(retPolicy.windows.memory_protection.mode).toEqual(ProtectionModes.detect); + expect(retPolicy.windows.popup.memory_protection.enabled).toBeFalsy(); + expect(retPolicy.windows.popup.memory_protection.message).toEqual(popupMessage); + }); + it('resets Platinum-paid malware fields for lower license tiers', () => { const defaults = policyFactory(); // reference const policy = policyFactory(); // what we will modify, and should be reset @@ -177,7 +237,9 @@ describe('policy_config and licenses', () => { expect(retPolicy.windows.popup.malware.message).not.toEqual(popupMessage); // need to invert the test, since it could be either value - expect(['', DefaultMalwareMessage]).toContain(retPolicy.windows.popup.malware.message); + expect(['', DefaultPolicyNotificationMessage]).toContain( + retPolicy.windows.popup.malware.message + ); }); it('resets Platinum-paid ransomware fields for lower license tiers', () => { @@ -195,7 +257,31 @@ describe('policy_config and licenses', () => { expect(retPolicy.windows.popup.ransomware.message).not.toEqual(popupMessage); // need to invert the test, since it could be either value - expect(['', DefaultMalwareMessage]).toContain(retPolicy.windows.popup.ransomware.message); + expect(['', DefaultPolicyNotificationMessage]).toContain( + retPolicy.windows.popup.ransomware.message + ); + }); + + it('resets Platinum-paid memory_protection fields for lower license tiers', () => { + const defaults = policyFactoryWithoutPaidFeatures(); // reference + const policy = policyFactory(); // what we will modify, and should be reset + const popupMessage = 'WOOP WOOP'; + policy.windows.popup.memory_protection.message = popupMessage; + + const retPolicy = unsetPolicyFeaturesAccordingToLicenseLevel(policy, Gold); + + expect(retPolicy.windows.memory_protection.mode).toEqual( + defaults.windows.memory_protection.mode + ); + expect(retPolicy.windows.popup.memory_protection.enabled).toEqual( + defaults.windows.popup.memory_protection.enabled + ); + expect(retPolicy.windows.popup.memory_protection.message).not.toEqual(popupMessage); + + // need to invert the test, since it could be either value + expect(['', DefaultPolicyNotificationMessage]).toContain( + retPolicy.windows.popup.memory_protection.message + ); }); it('sets ransomware supported field to false when license is below Platinum', () => { @@ -217,6 +303,30 @@ describe('policy_config and licenses', () => { expect(retPolicy.windows.ransomware.supported).toEqual(defaults.windows.ransomware.supported); }); + + it('sets memory_protection supported field to false when license is below Platinum', () => { + const defaults = policyFactoryWithoutPaidFeatures(); // reference + const policy = policyFactory(); // what we will modify, and should be reset + policy.windows.memory_protection.supported = true; + + const retPolicy = unsetPolicyFeaturesAccordingToLicenseLevel(policy, Gold); + + expect(retPolicy.windows.memory_protection.supported).toEqual( + defaults.windows.memory_protection.supported + ); + }); + + it('sets memory_protection supported field to true when license is at Platinum', () => { + const defaults = policyFactoryWithSupportedFeatures(); // reference + const policy = policyFactory(); // what we will modify, and should be reset + policy.windows.memory_protection.supported = false; + + const retPolicy = unsetPolicyFeaturesAccordingToLicenseLevel(policy, Platinum); + + expect(retPolicy.windows.memory_protection.supported).toEqual( + defaults.windows.memory_protection.supported + ); + }); }); describe('policyFactoryWithoutPaidFeatures for gold and below license', () => { diff --git a/x-pack/plugins/security_solution/common/license/policy_config.ts b/x-pack/plugins/security_solution/common/license/policy_config.ts index 171f2d9d0287df..759820df924ef6 100644 --- a/x-pack/plugins/security_solution/common/license/policy_config.ts +++ b/x-pack/plugins/security_solution/common/license/policy_config.ts @@ -9,7 +9,7 @@ import { ILicense } from '../../../licensing/common/types'; import { isAtLeast } from './license'; import { PolicyConfig } from '../endpoint/types'; import { - DefaultMalwareMessage, + DefaultPolicyNotificationMessage, policyFactoryWithoutPaidFeatures, policyFactoryWithSupportedFeatures, } from '../endpoint/models/policy_config'; @@ -30,6 +30,13 @@ export const isEndpointPolicyValidForLicense = ( return false; } + // only platinum or higher may enable ransomware + if ( + policy.windows.memory_protection.supported !== defaults.windows.memory_protection.supported + ) { + return false; + } + return true; // currently, platinum allows all features } @@ -46,12 +53,18 @@ export const isEndpointPolicyValidForLicense = ( // Only Platinum or higher may change the malware message (which can be blank or what Endpoint defaults) if ( [policy.windows, policy.mac].some( - (p) => p.popup.malware.message !== '' && p.popup.malware.message !== DefaultMalwareMessage + (p) => + p.popup.malware.message !== '' && + p.popup.malware.message !== DefaultPolicyNotificationMessage ) ) { return false; } + // only platinum or higher may enable ransomware + if (policy.windows.ransomware.mode !== defaults.windows.ransomware.mode) { + return false; + } // only platinum or higher may enable ransomware if (policy.windows.ransomware.mode !== defaults.windows.ransomware.mode) { return false; @@ -65,7 +78,7 @@ export const isEndpointPolicyValidForLicense = ( // Only Platinum or higher may change the ransomware message (which can be blank or what Endpoint defaults) if ( policy.windows.popup.ransomware.message !== '' && - policy.windows.popup.ransomware.message !== DefaultMalwareMessage + policy.windows.popup.ransomware.message !== DefaultPolicyNotificationMessage ) { return false; } @@ -74,6 +87,31 @@ export const isEndpointPolicyValidForLicense = ( if (policy.windows.ransomware.supported !== defaults.windows.ransomware.supported) { return false; } + // only platinum or higher may enable memory_protection + if (policy.windows.memory_protection.mode !== defaults.windows.memory_protection.mode) { + return false; + } + + // only platinum or higher may enable memory_protection notification + if ( + policy.windows.popup.memory_protection.enabled !== + defaults.windows.popup.memory_protection.enabled + ) { + return false; + } + + // Only Platinum or higher may change the memory_protection message (which can be blank or what Endpoint defaults) + if ( + policy.windows.popup.memory_protection.message !== '' && + policy.windows.popup.memory_protection.message !== DefaultPolicyNotificationMessage + ) { + return false; + } + + // only platinum or higher may enable memory_protection + if (policy.windows.memory_protection.supported !== defaults.windows.memory_protection.supported) { + return false; + } return true; }; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/index.test.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/index.test.ts index 94208390b660b5..4b3f02ec6f2d0c 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/index.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/index.test.ts @@ -284,6 +284,7 @@ describe('policy details: ', () => { security: true, }, malware: { mode: 'prevent' }, + memory_protection: { mode: 'off', supported: false }, ransomware: { mode: 'off', supported: false }, popup: { malware: { @@ -294,6 +295,10 @@ describe('policy details: ', () => { enabled: false, message: '', }, + memory_protection: { + enabled: false, + message: '', + }, }, logging: { file: 'info' }, antivirus_registration: { diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts index 793d083400aa2f..628d0ee53655f1 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/store/policy_details/middleware.ts @@ -6,7 +6,7 @@ */ import { IHttpFetchError } from 'kibana/public'; -import { DefaultMalwareMessage } from '../../../../../../common/endpoint/models/policy_config'; +import { DefaultPolicyNotificationMessage } from '../../../../../../common/endpoint/models/policy_config'; import { PolicyDetailsState, UpdatePolicyResponse } from '../../types'; import { policyIdFromParams, @@ -39,12 +39,17 @@ export const policyDetailsMiddlewareFactory: ImmutableMiddlewareFactory UIPolicyConfig = createSel events: windows.events, malware: windows.malware, ransomware: windows.ransomware, + memory_protection: windows.memory_protection, popup: windows.popup, antivirus_registration: windows.antivirus_registration, }, diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/types.ts b/x-pack/plugins/security_solution/public/management/pages/policy/types.ts index 269e70b3c24745..fe549b345ee885 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/types.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/types.ts @@ -114,6 +114,12 @@ export type MalwareProtectionOSes = KeysByValueCriteria< { malware: ProtectionFields } >; +/** Returns an array of the policy OSes that have a memory protection field */ +export type MemoryProtectionOSes = KeysByValueCriteria< + UIPolicyConfig, + { memory_protection: ProtectionFields } +>; + /** Returns an array of the policy OSes that have a ransomware protection field */ export type RansomwareProtectionOSes = KeysByValueCriteria< UIPolicyConfig, @@ -121,7 +127,7 @@ export type RansomwareProtectionOSes = KeysByValueCriteria< >; export type PolicyProtection = - | keyof Pick + | keyof Pick | keyof Pick | keyof Pick; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx index 93cf0f370a7159..40448d473ccf95 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details.test.tsx @@ -297,6 +297,16 @@ describe('Policy Details', () => { expect(tooltip).toHaveLength(1); }); + it('memory protection card and user notification checkbox are shown', () => { + const memory = policyView.find('EuiPanel[data-test-subj="memoryProtectionsForm"]'); + const userNotificationCheckbox = policyView.find( + 'EuiCheckbox[data-test-subj="memory_protectionUserNotificationCheckbox"]' + ); + + expect(memory).toHaveLength(1); + expect(userNotificationCheckbox).toHaveLength(1); + }); + it('ransomware card is shown', () => { const ransomware = policyView.find('EuiPanel[data-test-subj="ransomwareProtectionsForm"]'); expect(ransomware).toHaveLength(1); @@ -321,6 +331,15 @@ describe('Policy Details', () => { expect(tooltip).toHaveLength(0); }); + it('memory protection card, and user notification checkbox are hidden', () => { + const memory = policyView.find('EuiPanel[data-test-subj="memoryProtectionsForm"]'); + expect(memory).toHaveLength(0); + const userNotificationCheckbox = policyView.find( + 'EuiCheckbox[data-test-subj="memoryUserNotificationCheckbox"]' + ); + expect(userNotificationCheckbox).toHaveLength(0); + }); + it('ransomware card is hidden', () => { const ransomware = policyView.find('EuiPanel[data-test-subj="ransomwareProtectionsForm"]'); expect(ransomware).toHaveLength(0); @@ -328,7 +347,7 @@ describe('Policy Details', () => { it('shows the locked card in place of 1 paid feature', () => { const lockedCard = policyView.find('EuiCard[data-test-subj="lockedPolicyCard"]'); - expect(lockedCard).toHaveLength(1); + expect(lockedCard).toHaveLength(2); }); }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details_form.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details_form.tsx index 528f3afc1e64ae..dbbf8f2ab53241 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details_form.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_details_form.tsx @@ -8,7 +8,9 @@ import { EuiButtonEmpty, EuiSpacer, EuiText } from '@elastic/eui'; import React, { memo, useCallback, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; +import { i18n } from '@kbn/i18n'; import { MalwareProtections } from './policy_forms/protections/malware'; +import { MemoryProtection } from './policy_forms/protections/memory'; import { LinuxEvents, MacEvents, WindowsEvents } from './policy_forms/events'; import { AdvancedPolicyForms } from './policy_advanced'; import { AntivirusRegistrationForm } from './components/antivirus_registration_form'; @@ -16,6 +18,20 @@ import { Ransomware } from './policy_forms/protections/ransomware'; import { LockedPolicyCard } from './policy_forms/locked_card'; import { useLicense } from '../../../../common/hooks/use_license'; +const LOCKED_CARD_RAMSOMWARE_TITLE = i18n.translate( + 'xpack.securitySolution.endpoint.policy.details.ransomware', + { + defaultMessage: 'Ransomware', + } +); + +const LOCKED_CARD_MEMORY_TITLE = i18n.translate( + 'xpack.securitySolution.endpoint.policy.details.memory', + { + defaultMessage: 'Memory', + } +); + export const PolicyDetailsForm = memo(() => { const [showAdvancedPolicy, setShowAdvancedPolicy] = useState(false); const handleAdvancedPolicyClick = useCallback(() => { @@ -37,7 +53,13 @@ export const PolicyDetailsForm = memo(() => { - {isPlatinumPlus ? : } + {isPlatinumPlus ? : } + + {isPlatinumPlus ? ( + + ) : ( + + )} diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/locked_card.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/locked_card.tsx index e9e9195b819d34..150ae5e82ef558 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/locked_card.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/locked_card.tsx @@ -30,7 +30,7 @@ const LockedPolicyDiv = styled.div` } `; -export const LockedPolicyCard = memo(() => { +export const LockedPolicyCard = memo(({ title }: { title: string }) => { return ( { icon={} title={

- - - + {title}

} description={false} @@ -67,8 +62,8 @@ export const LockedPolicyCard = memo(() => {

{ + const OSes: Immutable = [OS.windows]; + const protection = 'memory_protection'; + return ( + } + > + + + + + + + + ), + }} + /> + + + ); +}); + +MemoryProtection.displayName = 'MemoryProtection'; diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/popup_options_to_versions.ts b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/popup_options_to_versions.ts index e13601c5a2bf22..04042a4f1d9a5f 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/popup_options_to_versions.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/policy_forms/protections/popup_options_to_versions.ts @@ -8,6 +8,7 @@ const popupVersions: Array<[string, string]> = [ ['malware', '7.11+'], ['ransomware', '7.12+'], + ['memory_protection', '7.15+'], ]; export const popupVersionsMap: ReadonlyMap = new Map(popupVersions); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 13cb175ac908e7..eed481aff3c240 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -20005,7 +20005,6 @@ "xpack.securitySolution.endpoint.policy.details.detectionRulesMessage": "{detectionRulesLink}を表示します。事前構築済みルールは、[検出ルール]ページで「Elastic」というタグが付けられています。", "xpack.securitySolution.endpoint.policy.details.eventCollection": "イベント収集", "xpack.securitySolution.endpoint.policy.details.eventCollectionsEnabled": "{selected} / {total} 件のイベント収集が有効です", - "xpack.securitySolution.endpoint.policy.details.lockedCard": "ランサムウェア対策をオンにするには、ライセンスをプラチナに更新するか、30日間の無料トライアルを開始するか、AWS、GCP、またはAzureで{cloudDeploymentLink}にサインアップしてください。", "xpack.securitySolution.endpoint.policy.details.malware": "マルウェア", "xpack.securitySolution.endpoint.policy.details.platinum": "プラチナ", "xpack.securitySolution.endpoint.policy.details.prevent": "防御", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 6e7ab7a33a0f3f..cc288ebecdc747 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -20305,7 +20305,6 @@ "xpack.securitySolution.endpoint.policy.details.detectionRulesMessage": "请查看{detectionRulesLink}。在“检测规则”页面上,预置规则标记有“Elastic”。", "xpack.securitySolution.endpoint.policy.details.eventCollection": "事件收集", "xpack.securitySolution.endpoint.policy.details.eventCollectionsEnabled": "{selected} / {total} 个事件收集已启用", - "xpack.securitySolution.endpoint.policy.details.lockedCard": "要打开勒索软件防护,必须将您的许可证升级到白金级、开始 30 天免费试用或在 AWS、GCP 或 Azure 中实施{cloudDeploymentLink}。", "xpack.securitySolution.endpoint.policy.details.malware": "恶意软件", "xpack.securitySolution.endpoint.policy.details.platinum": "白金级", "xpack.securitySolution.endpoint.policy.details.prevent": "防御", diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts index ae60935013d272..6d06b31eddecdd 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts @@ -329,12 +329,17 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }, logging: { file: 'info' }, malware: { mode: 'prevent' }, + memory_protection: { mode: 'prevent', supported: true }, ransomware: { mode: 'prevent', supported: true }, popup: { malware: { enabled: true, message: 'Elastic Security {action} {filename}', }, + memory_protection: { + enabled: true, + message: 'Elastic Security {action} {filename}', + }, ransomware: { enabled: true, message: 'Elastic Security {action} {filename}', @@ -533,12 +538,17 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }, logging: { file: 'info' }, malware: { mode: 'prevent' }, + memory_protection: { mode: 'prevent', supported: true }, ransomware: { mode: 'prevent', supported: true }, popup: { malware: { enabled: true, message: 'Elastic Security {action} {filename}', }, + memory_protection: { + enabled: true, + message: 'Elastic Security {action} {filename}', + }, ransomware: { enabled: true, message: 'Elastic Security {action} {filename}', @@ -734,12 +744,17 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }, logging: { file: 'info' }, malware: { mode: 'prevent' }, + memory_protection: { mode: 'prevent', supported: true }, ransomware: { mode: 'prevent', supported: true }, popup: { malware: { enabled: true, message: 'Elastic Security {action} {filename}', }, + memory_protection: { + enabled: true, + message: 'Elastic Security {action} {filename}', + }, ransomware: { enabled: true, message: 'Elastic Security {action} {filename}',