Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solution] Add supported field to ransomware #100135

Merged
merged 11 commits into from
May 19, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const policyFactory = (): PolicyConfig => {
},
ransomware: {
mode: ProtectionModes.prevent,
supported: true,
},
popup: {
malware: {
Expand Down Expand Up @@ -89,6 +90,7 @@ export const policyFactoryWithoutPaidFeatures = (
...policy.windows,
ransomware: {
mode: ProtectionModes.off,
supported: false,
},
popup: {
...policy.windows.popup,
Expand All @@ -115,6 +117,24 @@ export const policyFactoryWithoutPaidFeatures = (
};
};

/**
* Strips paid features from an existing or new `PolicyConfig` for gold and below license
*/
export const policyFactoryWithSupportedFeatures = (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason why you created another function as opposed to just updating the existing policyFactory (policy config with defaults for platinum licenses) and the policyFactoryWithoutPaidFeatures (policy config with defaults for gold and below licenses) windows.ransomware.supported fields?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also use this function to correctly set the supported when the license changes. I want to ensure that I'm only changing the fields that I want to. I broke it out to its own function so that I didn't resent everything to the default.

You can see how it's used here: https://github.com/elastic/kibana/pull/100135/files#diff-6a6ff8e750469752a4993b278b7023af4bef43a49ccee59c3933e3d7c68c78f5R26

Also here in a test: https://github.com/elastic/kibana/pull/100135/files#diff-861c70c43696ed0ea535a55a47a316c434a14510046d2eb623d6b228d32e4c8eR211

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gotcha!

policy: PolicyConfig = policyFactory()
): PolicyConfig => {
return {
...policy,
windows: {
...policy.windows,
ransomware: {
...policy.windows.ransomware,
supported: true,
},
},
};
};

/**
* Reflects what string the Endpoint will use when message field is default/empty
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -835,7 +835,7 @@ export interface PolicyConfig {
security: boolean;
};
malware: ProtectionFields;
ransomware: ProtectionFields;
ransomware: ProtectionFields & SupportedFields;
logging: {
file: string;
};
Expand Down Expand Up @@ -910,6 +910,11 @@ export interface ProtectionFields {
mode: ProtectionModes;
}

/** Policy: Supported fields */
export interface SupportedFields {
supported: boolean;
}

/** Policy protection mode options */
export enum ProtectionModes {
detect = 'detect',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import {
isEndpointPolicyValidForLicense,
unsetPolicyFeaturesAboveLicenseLevel,
unsetPolicyFeaturesAccordingToLicenseLevel,
} from './policy_config';
import {
DefaultMalwareMessage,
Expand Down Expand Up @@ -130,15 +130,15 @@ describe('policy_config and licenses', () => {
});
});

describe('unsetPolicyFeaturesAboveLicenseLevel', () => {
describe('unsetPolicyFeaturesAccordingToLicenseLevel', () => {
it('does not change any malware fields with a Platinum license', () => {
const policy = policyFactory();
const popupMessage = 'WOOP WOOP';
policy.windows.popup.malware.message = popupMessage;
policy.mac.popup.malware.message = popupMessage;
policy.windows.popup.malware.enabled = false;

const retPolicy = unsetPolicyFeaturesAboveLicenseLevel(policy, Platinum);
const retPolicy = unsetPolicyFeaturesAccordingToLicenseLevel(policy, Platinum);
expect(retPolicy.windows.popup.malware.enabled).toBeFalsy();
expect(retPolicy.windows.popup.malware.message).toEqual(popupMessage);
expect(retPolicy.mac.popup.malware.message).toEqual(popupMessage);
Expand All @@ -151,7 +151,7 @@ describe('policy_config and licenses', () => {
policy.windows.popup.ransomware.enabled = false;
policy.windows.popup.ransomware.message = popupMessage;

const retPolicy = unsetPolicyFeaturesAboveLicenseLevel(policy, Platinum);
const retPolicy = unsetPolicyFeaturesAccordingToLicenseLevel(policy, Platinum);
expect(retPolicy.windows.ransomware.mode).toEqual(ProtectionModes.detect);
expect(retPolicy.windows.popup.ransomware.enabled).toBeFalsy();
expect(retPolicy.windows.popup.ransomware.message).toEqual(popupMessage);
Expand All @@ -167,7 +167,7 @@ describe('policy_config and licenses', () => {

policy.windows.popup.ransomware.message = popupMessage;
policy.windows.popup.ransomware.enabled = false;
const retPolicy = unsetPolicyFeaturesAboveLicenseLevel(policy, Gold);
const retPolicy = unsetPolicyFeaturesAccordingToLicenseLevel(policy, Gold);
expect(retPolicy.windows.popup.malware.enabled).toEqual(
defaults.windows.popup.malware.enabled
);
Expand All @@ -183,7 +183,7 @@ describe('policy_config and licenses', () => {
const popupMessage = 'WOOP WOOP';
policy.windows.popup.ransomware.message = popupMessage;

const retPolicy = unsetPolicyFeaturesAboveLicenseLevel(policy, Gold);
const retPolicy = unsetPolicyFeaturesAccordingToLicenseLevel(policy, Gold);

expect(retPolicy.windows.ransomware.mode).toEqual(defaults.windows.ransomware.mode);
expect(retPolicy.windows.popup.ransomware.enabled).toEqual(
Expand Down
17 changes: 15 additions & 2 deletions x-pack/plugins/security_solution/common/license/policy_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { PolicyConfig } from '../endpoint/types';
import {
DefaultMalwareMessage,
policyFactoryWithoutPaidFeatures,
policyFactoryWithSupportedFeatures,
} from '../endpoint/models/policy_config';

/**
Expand All @@ -22,6 +23,13 @@ export const isEndpointPolicyValidForLicense = (
license: ILicense | null
): boolean => {
if (isAtLeast(license, 'platinum')) {
const defaults = policyFactoryWithSupportedFeatures();

// only platinum or higher may enable ransomware
if (policy.windows.ransomware.supported !== defaults.windows.ransomware.supported) {
return false;
}

return true; // currently, platinum allows all features
}

Expand Down Expand Up @@ -62,19 +70,24 @@ export const isEndpointPolicyValidForLicense = (
return false;
}

// only platinum or higher may enable ransomware
if (policy.windows.ransomware.supported !== defaults.windows.ransomware.supported) {
return false;
}

return true;
};

/**
* Resets paid features in a PolicyConfig back to default values
* when unsupported by the given license level.
*/
export const unsetPolicyFeaturesAboveLicenseLevel = (
export const unsetPolicyFeaturesAccordingToLicenseLevel = (
policy: PolicyConfig,
license: ILicense | null
): PolicyConfig => {
if (isAtLeast(license, 'platinum')) {
return policy;
return policyFactoryWithSupportedFeatures(policy);
}

// set any license-gated features back to the defaults
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import { createSelector } from 'reselect';
import { matchPath } from 'react-router-dom';
import { ILicense } from '../../../../../../../licensing/common/types';
import { unsetPolicyFeaturesAboveLicenseLevel } from '../../../../../../common/license/policy_config';
import { unsetPolicyFeaturesAccordingToLicenseLevel } from '../../../../../../common/license/policy_config';
import { PolicyDetailsState } from '../../types';
import {
Immutable,
Expand All @@ -33,7 +33,7 @@ export const licensedPolicy: (
licenseState,
(policyData, license) => {
if (policyData) {
const policyValue = unsetPolicyFeaturesAboveLicenseLevel(
const policyValue = unsetPolicyFeaturesAccordingToLicenseLevel(
policyData.inputs[0].config.policy.value,
license as ILicense
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { PackagePolicyServiceInterface } from '../../../../../fleet/server';
import { ILicense } from '../../../../../licensing/common/types';
import {
isEndpointPolicyValidForLicense,
unsetPolicyFeaturesAboveLicenseLevel,
unsetPolicyFeaturesAccordingToLicenseLevel,
} from '../../../../common/license/policy_config';
import { isAtLeast, LicenseService } from '../../../../common/license/license';

Expand Down Expand Up @@ -76,10 +76,6 @@ export class PolicyWatcher {
}

public async watch(license: ILicense) {
if (isAtLeast(license, 'platinum')) {
return;
}

let page = 1;
let response: {
items: PackagePolicy[];
Expand Down Expand Up @@ -114,7 +110,7 @@ export class PolicyWatcher {
};
const policyConfig = updatePolicy.inputs[0].config?.policy.value;
if (!isEndpointPolicyValidForLicense(policyConfig, license)) {
updatePolicy.inputs[0].config!.policy.value = unsetPolicyFeaturesAboveLicenseLevel(
updatePolicy.inputs[0].config!.policy.value = unsetPolicyFeaturesAccordingToLicenseLevel(
policyConfig,
license
);
Expand Down