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
2 changes: 2 additions & 0 deletions x-pack/plugins/fleet/server/saved_objects/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
migrateSettingsToV7130,
migrateOutputToV7130,
} from './migrations/to_v7_13_0';
import { migratePackagePolicyToV7140 } from './migrations/to_v7_14_0';

/*
* Saved object types and mappings
Expand Down Expand Up @@ -267,6 +268,7 @@ const getSavedObjectTypes = (
'7.11.0': migratePackagePolicyToV7110,
'7.12.0': migratePackagePolicyToV7120,
'7.13.0': migratePackagePolicyToV7130,
'7.14.0': migratePackagePolicyToV7140,
},
},
[PACKAGES_SAVED_OBJECT_TYPE]: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,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';
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
/*
* 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 { migrateEndpointPackagePolicyToV7140 } from './to_v7_14_0';

describe('7.14.0 Endpoint Package Policy migration', () => {
const migration = migrateEndpointPackagePolicyToV7140;
it('adds supported option for ransomware on migrations', () => {
const doc = {
Copy link
Contributor

Choose a reason for hiding this comment

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

Any chance you can collapse several of these document mocks into a builder function instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@paul-tavares my next PR is Linux Malware and will build on this migration test, I'll address this there.

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: {
ransomware: {
mode: 'off',
},
malware: {
mode: 'off',
},
popup: {
malware: {
message: '',
enabled: false,
},
ransomware: {
message: '',
enabled: false,
},
},
},
linux: {
events: { process: true, file: true, network: true },
logging: { file: 'info' },
},
},
},
},
},
],
},
type: ' nested',
};

expect(migration(doc, {} as SavedObjectMigrationContext)).toEqual({
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: {
ransomware: {
mode: 'off',
supported: true,
},
malware: {
mode: 'off',
},
popup: {
malware: {
message: '',
enabled: false,
},
ransomware: {
message: '',
enabled: false,
},
},
},
linux: {
events: { process: true, file: true, network: true },
logging: { file: 'info' },
},
},
},
},
},
],
},
type: ' nested',
id: 'mock-saved-object-id',
});
});

it('does not modify non-endpoint package policies', () => {
const doc: SavedObjectUnsanitizedDoc<PackagePolicy> = {
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<PackagePolicy>
).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',
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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 migrateEndpointPackagePolicyToV7140: SavedObjectMigrationFn<
PackagePolicy,
PackagePolicy
> = (packagePolicyDoc) => {
const updatedPackagePolicyDoc: SavedObjectUnsanitizedDoc<PackagePolicy> = cloneDeep(
packagePolicyDoc
);

if (packagePolicyDoc.attributes.package?.name === 'endpoint') {
const input = updatedPackagePolicyDoc.attributes.inputs[0];
if (input && input.config) {
const policy = input.config.policy.value;

// This value is based on license.
// For the migration, we add 'true', our license watcher will correct it, if needed, when the app starts.
policy.windows.ransomware.supported = true;
}
}

return updatedPackagePolicyDoc;
};
29 changes: 29 additions & 0 deletions x-pack/plugins/fleet/server/saved_objects/migrations/to_v7_14_0.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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 { migrateEndpointPackagePolicyToV7140 } from './security_solution';

export const migratePackagePolicyToV7140: SavedObjectMigrationFn<PackagePolicy, PackagePolicy> = (
packagePolicyDoc,
migrationContext
) => {
let updatedPackagePolicyDoc = packagePolicyDoc;

// Endpoint specific migrations
if (packagePolicyDoc.attributes.package?.name === 'endpoint') {
updatedPackagePolicyDoc = migrateEndpointPackagePolicyToV7140(
packagePolicyDoc,
migrationContext
);
}

return updatedPackagePolicyDoc;
};
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
Loading