From 652f31108359e458c43e2d057db90f0e023d8ea5 Mon Sep 17 00:00:00 2001 From: Dmitrii Shevchenko Date: Tue, 3 Dec 2024 13:11:24 +0100 Subject: [PATCH] [Security Solution] Skip isCustomized calculation when the feature flag is off (#201825) **Resolves: https://github.com/elastic/kibana/issues/201632** ## Summary When the rule customization feature flag is disabled, we should always return `isCustomized: false`, regardless of any changes introduced to a rule. This ensures that we do not accidentally mark prebuilt rules as customized in 8.16 with the feature flag off. For more details, refer to the related issue: https://github.com/elastic/kibana/issues/201632 ### Main Changes - The primary change in this PR is encapsulated in the `calculateIsCustomized` function - Other changes involve passing the feature flag to this function - Added integration tests to cover all API CRUD operations that can be performed with rules --- .../ftr_security_serverless_configs.yml | 3 +- .buildkite/ftr_security_stateful_configs.yml | 3 +- .../logic/bulk_actions/bulk_edit_rules.ts | 14 +- ...on_rules_client.create_custom_rule.test.ts | 1 + ..._rules_client.create_prebuilt_rule.test.ts | 1 + ...detection_rules_client.delete_rule.test.ts | 1 + ...detection_rules_client.import_rule.test.ts | 1 + ...etection_rules_client.import_rules.test.ts | 1 + .../detection_rules_client.patch_rule.test.ts | 1 + .../detection_rules_client.ts | 6 + ...detection_rules_client.update_rule.test.ts | 1 + ...rules_client.upgrade_prebuilt_rule.test.ts | 1 + .../mergers/apply_rule_patch.test.ts | 22 ++ .../mergers/apply_rule_patch.ts | 3 + .../mergers/apply_rule_update.ts | 3 + .../rule_source/calculate_is_customized.ts | 20 +- .../rule_source/calculate_rule_source.test.ts | 25 ++ .../rule_source/calculate_rule_source.ts | 8 +- .../methods/import_rule.ts | 3 + .../methods/patch_rule.ts | 3 + .../methods/update_rule.ts | 3 + .../methods/upgrade_prebuilt_rule.ts | 3 + .../calculate_rule_source_for_import.test.ts | 4 + .../calculate_rule_source_for_import.ts | 3 + .../calculate_rule_source_from_asset.test.ts | 4 + .../calculate_rule_source_from_asset.ts | 10 +- .../rule_source_importer.test.ts | 2 + .../rule_source_importer.ts | 2 + .../server/request_context_factory.ts | 1 + .../configs/ess.config.ts | 25 ++ .../configs/serverless.config.ts | 17 ++ .../customization_disabled/index.ts | 14 ++ .../is_customized_calculation.ts | 218 ++++++++++++++++++ .../configs/ess.config.ts | 2 +- .../configs/serverless.config.ts | 2 +- .../import_rules.ts | 0 .../index.ts | 2 +- .../is_customized_calculation.ts | 0 .../rules_export.ts | 0 .../patch_rules.ts | 28 +-- 40 files changed, 425 insertions(+), 36 deletions(-) create mode 100644 x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/configs/ess.config.ts create mode 100644 x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/configs/serverless.config.ts create mode 100644 x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/index.ts create mode 100644 x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/is_customized_calculation.ts rename x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/{trial_license_complete_tier => customization_enabled}/configs/ess.config.ts (91%) rename x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/{trial_license_complete_tier => customization_enabled}/configs/serverless.config.ts (84%) rename x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/{trial_license_complete_tier => customization_enabled}/import_rules.ts (100%) rename x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/{trial_license_complete_tier => customization_enabled}/index.ts (94%) rename x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/{trial_license_complete_tier => customization_enabled}/is_customized_calculation.ts (100%) rename x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/{trial_license_complete_tier => customization_enabled}/rules_export.ts (100%) diff --git a/.buildkite/ftr_security_serverless_configs.yml b/.buildkite/ftr_security_serverless_configs.yml index 920ecce349356..74d82d40c8bce 100644 --- a/.buildkite/ftr_security_serverless_configs.yml +++ b/.buildkite/ftr_security_serverless_configs.yml @@ -76,7 +76,8 @@ enabled: - x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/large_prebuilt_rules_package/trial_license_complete_tier/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/update_prebuilt_rules_package/trial_license_complete_tier/configs/serverless.config.ts - - x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/trial_license_complete_tier/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_bulk_actions/trial_license_complete_tier/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_delete/trial_license_complete_tier/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_delete/basic_license_essentials_tier/configs/serverless.config.ts diff --git a/.buildkite/ftr_security_stateful_configs.yml b/.buildkite/ftr_security_stateful_configs.yml index f7caacba05e1b..46c6f356b3ae4 100644 --- a/.buildkite/ftr_security_stateful_configs.yml +++ b/.buildkite/ftr_security_stateful_configs.yml @@ -58,7 +58,8 @@ enabled: - x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/large_prebuilt_rules_package/trial_license_complete_tier/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/update_prebuilt_rules_package/trial_license_complete_tier/configs/ess.config.ts - - x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/trial_license_complete_tier/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_bulk_actions/trial_license_complete_tier/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_delete/trial_license_complete_tier/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_delete/basic_license_essentials_tier/configs/ess.config.ts diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/bulk_actions/bulk_edit_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/bulk_actions/bulk_edit_rules.ts index bee44a3600f97..b9c97c2412b1a 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/bulk_actions/bulk_edit_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/bulk_actions/bulk_edit_rules.ts @@ -92,14 +92,20 @@ export const bulkEditRules = async ({ params: modifiedParams, }; const ruleResponse = convertAlertingRuleToRuleResponse(updatedRule); + let isCustomized = false; + if (ruleResponse.immutable === true) { + isCustomized = calculateIsCustomized({ + baseRule: baseVersionsMap.get(ruleResponse.rule_id), + nextRule: ruleResponse, + isRuleCustomizationEnabled: experimentalFeatures.prebuiltRulesCustomizationEnabled, + }); + } + const ruleSource = ruleResponse.immutable === true ? { type: 'external' as const, - isCustomized: calculateIsCustomized( - baseVersionsMap.get(ruleResponse.rule_id), - ruleResponse - ), + isCustomized, } : { type: 'internal' as const, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.create_custom_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.create_custom_rule.test.ts index af3f85f3e1345..b07f95f74d2f9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.create_custom_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.create_custom_rule.test.ts @@ -51,6 +51,7 @@ describe('DetectionRulesClient.createCustomRule', () => { rulesClient, mlAuthz, savedObjectsClient, + isRuleCustomizationEnabled: true, }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.create_prebuilt_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.create_prebuilt_rule.test.ts index 4caeb4bd9b940..6e1e75855b6a0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.create_prebuilt_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.create_prebuilt_rule.test.ts @@ -44,6 +44,7 @@ describe('DetectionRulesClient.createPrebuiltRule', () => { rulesClient, mlAuthz, savedObjectsClient, + isRuleCustomizationEnabled: true, }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.delete_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.delete_rule.test.ts index ea5d1453753c3..a4afe5abadb6b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.delete_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.delete_rule.test.ts @@ -30,6 +30,7 @@ describe('DetectionRulesClient.deleteRule', () => { rulesClient, mlAuthz, savedObjectsClient, + isRuleCustomizationEnabled: true, }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.import_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.import_rule.test.ts index 4300b17b3be80..5f8bfb5577740 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.import_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.import_rule.test.ts @@ -51,6 +51,7 @@ describe('DetectionRulesClient.importRule', () => { rulesClient, mlAuthz, savedObjectsClient, + isRuleCustomizationEnabled: true, }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.import_rules.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.import_rules.test.ts index 58b1385dda09c..3dfd859fb0b7b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.import_rules.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.import_rules.test.ts @@ -32,6 +32,7 @@ describe('detectionRulesClient.importRules', () => { rulesClient: rulesClientMock.create(), mlAuthz: buildMlAuthz(), savedObjectsClient: savedObjectsClientMock.create(), + isRuleCustomizationEnabled: true, }); (checkRuleExceptionReferences as jest.Mock).mockReturnValue([[], []]); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.patch_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.patch_rule.test.ts index 448df6b581a3b..18fe3c54da7e0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.patch_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.patch_rule.test.ts @@ -50,6 +50,7 @@ describe('DetectionRulesClient.patchRule', () => { rulesClient, mlAuthz, savedObjectsClient, + isRuleCustomizationEnabled: true, }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.ts index fb6f5c4a03c1f..57c8f12c09aa9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.ts @@ -38,6 +38,7 @@ interface DetectionRulesClientParams { rulesClient: RulesClient; savedObjectsClient: SavedObjectsClientContract; mlAuthz: MlAuthz; + isRuleCustomizationEnabled: boolean; } export const createDetectionRulesClient = ({ @@ -45,6 +46,7 @@ export const createDetectionRulesClient = ({ rulesClient, mlAuthz, savedObjectsClient, + isRuleCustomizationEnabled, }: DetectionRulesClientParams): IDetectionRulesClient => { const prebuiltRuleAssetClient = createPrebuiltRuleAssetsClient(savedObjectsClient); @@ -89,6 +91,7 @@ export const createDetectionRulesClient = ({ prebuiltRuleAssetClient, mlAuthz, ruleUpdate, + isRuleCustomizationEnabled, }); }); }, @@ -101,6 +104,7 @@ export const createDetectionRulesClient = ({ prebuiltRuleAssetClient, mlAuthz, rulePatch, + isRuleCustomizationEnabled, }); }); }, @@ -119,6 +123,7 @@ export const createDetectionRulesClient = ({ ruleAsset, mlAuthz, prebuiltRuleAssetClient, + isRuleCustomizationEnabled, }); }); }, @@ -131,6 +136,7 @@ export const createDetectionRulesClient = ({ importRulePayload: args, mlAuthz, prebuiltRuleAssetClient, + isRuleCustomizationEnabled, }); }); }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.update_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.update_rule.test.ts index cbd0fb1fe3680..64c01ab395529 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.update_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.update_rule.test.ts @@ -50,6 +50,7 @@ describe('DetectionRulesClient.updateRule', () => { rulesClient, mlAuthz, savedObjectsClient, + isRuleCustomizationEnabled: true, }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.upgrade_prebuilt_rule.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.upgrade_prebuilt_rule.test.ts index acdb7b9653930..ed186abe5a7c3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.upgrade_prebuilt_rule.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/detection_rules_client.upgrade_prebuilt_rule.test.ts @@ -48,6 +48,7 @@ describe('DetectionRulesClient.upgradePrebuiltRule', () => { rulesClient, mlAuthz, savedObjectsClient, + isRuleCustomizationEnabled: true, }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_patch.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_patch.test.ts index abf90c3f4dfc4..0d780c07aac19 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_patch.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_patch.test.ts @@ -37,6 +37,7 @@ describe('applyRulePatch', () => { rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled: true, }); expect(patchedRule).toEqual( expect.objectContaining({ @@ -65,6 +66,7 @@ describe('applyRulePatch', () => { rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled: true, }); expect(patchedRule).toEqual( expect.objectContaining({ @@ -94,6 +96,7 @@ describe('applyRulePatch', () => { rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled: true, }) ).rejects.toThrowError( 'event_category_override: Expected string, received number, tiebreaker_field: Expected string, received number, timestamp_field: Expected string, received number' @@ -119,6 +122,7 @@ describe('applyRulePatch', () => { rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled: true, }) ).rejects.toThrowError('alert_suppression.group_by: Expected array, received string'); }); @@ -134,6 +138,7 @@ describe('applyRulePatch', () => { rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled: true, }); expect(patchedRule).toEqual( expect.objectContaining({ @@ -154,6 +159,7 @@ describe('applyRulePatch', () => { rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled: true, }) ).rejects.toThrowError( 'threat_query: Expected string, received number, threat_indicator_path: Expected string, received number' @@ -170,6 +176,7 @@ describe('applyRulePatch', () => { rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled: true, }); expect(patchedRule).toEqual( expect.objectContaining({ @@ -190,6 +197,7 @@ describe('applyRulePatch', () => { rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled: true, }) ).rejects.toThrowError( "index.0: Expected string, received number, language: Invalid enum value. Expected 'kuery' | 'lucene', received 'non-language'" @@ -206,6 +214,7 @@ describe('applyRulePatch', () => { rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled: true, }); expect(patchedRule).toEqual( expect.objectContaining({ @@ -226,6 +235,7 @@ describe('applyRulePatch', () => { rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled: true, }) ).rejects.toThrowError( "index.0: Expected string, received number, language: Invalid enum value. Expected 'kuery' | 'lucene', received 'non-language'" @@ -244,6 +254,7 @@ describe('applyRulePatch', () => { rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled: true, }); expect(patchedRule).toEqual( expect.objectContaining({ @@ -268,6 +279,7 @@ describe('applyRulePatch', () => { rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled: true, }) ).rejects.toThrowError('threshold.value: Expected number, received string'); }); @@ -285,6 +297,7 @@ describe('applyRulePatch', () => { rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled: true, }); expect(patchedRule).toEqual( expect.objectContaining({ @@ -308,6 +321,7 @@ describe('applyRulePatch', () => { rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled: true, }); expect(patchedRule).toEqual( expect.objectContaining({ @@ -330,6 +344,7 @@ describe('applyRulePatch', () => { rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled: true, }); expect(patchedRule).toEqual( expect.objectContaining({ @@ -354,6 +369,7 @@ describe('applyRulePatch', () => { rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled: true, }); expect(patchedRule).toEqual( expect.objectContaining({ @@ -376,6 +392,7 @@ describe('applyRulePatch', () => { rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled: true, }); expect(patchedRule).toEqual( expect.objectContaining({ @@ -394,6 +411,7 @@ describe('applyRulePatch', () => { rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled: true, }) ).rejects.toThrowError('anomaly_threshold: Expected number, received string'); }); @@ -410,6 +428,7 @@ describe('applyRulePatch', () => { rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled: true, }); expect(patchedRule).toEqual( @@ -432,6 +451,7 @@ describe('applyRulePatch', () => { rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled: true, }); expect(patchedRule).toEqual( expect.objectContaining({ @@ -450,6 +470,7 @@ describe('applyRulePatch', () => { rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled: true, }) ).rejects.toThrowError('new_terms_fields: Expected array, received string'); }); @@ -472,6 +493,7 @@ describe('applyRulePatch', () => { rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled: true, }); expect(patchedRule).toEqual( expect.objectContaining({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_patch.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_patch.ts index 9f5b167322491..aaaab474c2fbe 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_patch.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_patch.ts @@ -51,6 +51,7 @@ interface ApplyRulePatchProps { prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient; existingRule: RuleResponse; rulePatch: PatchRuleRequestBody; + isRuleCustomizationEnabled: boolean; } // eslint-disable-next-line complexity @@ -58,6 +59,7 @@ export const applyRulePatch = async ({ rulePatch, existingRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled, }: ApplyRulePatchProps): Promise => { const typeSpecificParams = patchTypeSpecificParams(rulePatch, existingRule); @@ -122,6 +124,7 @@ export const applyRulePatch = async ({ nextRule.rule_source = await calculateRuleSource({ rule: nextRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled, }); return nextRule; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_update.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_update.ts index b911e66a1fc45..850924d3cd04d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_update.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/apply_rule_update.ts @@ -17,12 +17,14 @@ interface ApplyRuleUpdateProps { prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient; existingRule: RuleResponse; ruleUpdate: RuleUpdateProps; + isRuleCustomizationEnabled: boolean; } export const applyRuleUpdate = async ({ prebuiltRuleAssetClient, existingRule, ruleUpdate, + isRuleCustomizationEnabled, }: ApplyRuleUpdateProps): Promise => { const nextRule: RuleResponse = { ...applyRuleDefaults(ruleUpdate), @@ -46,6 +48,7 @@ export const applyRuleUpdate = async ({ nextRule.rule_source = await calculateRuleSource({ rule: nextRule, prebuiltRuleAssetClient, + isRuleCustomizationEnabled, }); return nextRule; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/rule_source/calculate_is_customized.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/rule_source/calculate_is_customized.ts index 92c7d941dd7f1..a94506486cc53 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/rule_source/calculate_is_customized.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/rule_source/calculate_is_customized.ts @@ -12,10 +12,22 @@ import { calculateRuleFieldsDiff } from '../../../../../prebuilt_rules/logic/dif import { convertRuleToDiffable } from '../../../../../../../../common/detection_engine/prebuilt_rules/diff/convert_rule_to_diffable'; import { convertPrebuiltRuleAssetToRuleResponse } from '../../converters/convert_prebuilt_rule_asset_to_rule_response'; -export function calculateIsCustomized( - baseRule: PrebuiltRuleAsset | undefined, - nextRule: RuleResponse -) { +interface CalculateIsCustomizedArgs { + baseRule: PrebuiltRuleAsset | undefined; + nextRule: RuleResponse; + isRuleCustomizationEnabled: boolean; +} + +export function calculateIsCustomized({ + baseRule, + nextRule, + isRuleCustomizationEnabled, +}: CalculateIsCustomizedArgs) { + if (!isRuleCustomizationEnabled) { + // We don't want to accidentally mark rules as customized when customization is disabled. + return false; + } + if (baseRule == null) { // If the base version is missing, we consider the rule to be customized return true; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/rule_source/calculate_rule_source.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/rule_source/calculate_rule_source.test.ts index e44c69d2705d5..d0b9f722458a1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/rule_source/calculate_rule_source.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/rule_source/calculate_rule_source.test.ts @@ -43,6 +43,7 @@ describe('calculateRuleSource', () => { const result = await calculateRuleSource({ prebuiltRuleAssetClient, rule, + isRuleCustomizationEnabled: true, }); expect(result).toEqual({ type: 'internal', @@ -59,6 +60,7 @@ describe('calculateRuleSource', () => { const result = await calculateRuleSource({ prebuiltRuleAssetClient, rule, + isRuleCustomizationEnabled: true, }); expect(result).toEqual( expect.objectContaining({ @@ -79,6 +81,7 @@ describe('calculateRuleSource', () => { const result = await calculateRuleSource({ prebuiltRuleAssetClient, rule, + isRuleCustomizationEnabled: true, }); expect(result).toEqual( expect.objectContaining({ @@ -101,6 +104,28 @@ describe('calculateRuleSource', () => { const result = await calculateRuleSource({ prebuiltRuleAssetClient, rule, + isRuleCustomizationEnabled: true, + }); + expect(result).toEqual( + expect.objectContaining({ + type: 'external', + is_customized: false, + }) + ); + }); + + it('returns is_customized false when the rule is customized but customization is disabled', async () => { + const rule = getSampleRule(); + rule.immutable = true; + rule.name = 'Updated name'; + + const baseRule = getSampleRuleAsset(); + prebuiltRuleAssetClient.fetchAssetsByVersion.mockResolvedValueOnce([baseRule]); + + const result = await calculateRuleSource({ + prebuiltRuleAssetClient, + rule, + isRuleCustomizationEnabled: false, }); expect(result).toEqual( expect.objectContaining({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/rule_source/calculate_rule_source.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/rule_source/calculate_rule_source.ts index 742cd20544a60..6c3d2419159a6 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/rule_source/calculate_rule_source.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/mergers/rule_source/calculate_rule_source.ts @@ -16,11 +16,13 @@ import { calculateIsCustomized } from './calculate_is_customized'; interface CalculateRuleSourceProps { prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient; rule: RuleResponse; + isRuleCustomizationEnabled: boolean; } export async function calculateRuleSource({ prebuiltRuleAssetClient, rule, + isRuleCustomizationEnabled, }: CalculateRuleSourceProps): Promise { if (rule.immutable) { // This is a prebuilt rule and, despite the name, they are not immutable. So @@ -33,7 +35,11 @@ export async function calculateRuleSource({ ]); const baseRule: PrebuiltRuleAsset | undefined = prebuiltRulesResponse.at(0); - const isCustomized = calculateIsCustomized(baseRule, rule); + const isCustomized = calculateIsCustomized({ + baseRule, + nextRule: rule, + isRuleCustomizationEnabled, + }); return { type: 'external', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/import_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/import_rule.ts index dd57e66c41a64..16fda89391ab9 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/import_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/import_rule.ts @@ -26,6 +26,7 @@ interface ImportRuleOptions { prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient; importRulePayload: ImportRuleArgs; mlAuthz: MlAuthz; + isRuleCustomizationEnabled: boolean; } export const importRule = async ({ @@ -34,6 +35,7 @@ export const importRule = async ({ importRulePayload, prebuiltRuleAssetClient, mlAuthz, + isRuleCustomizationEnabled, }: ImportRuleOptions): Promise => { const { ruleToImport, overwriteRules, overrideFields, allowMissingConnectorSecrets } = importRulePayload; @@ -60,6 +62,7 @@ export const importRule = async ({ prebuiltRuleAssetClient, existingRule, ruleUpdate: rule, + isRuleCustomizationEnabled, }); // applyRuleUpdate prefers the existing rule's values for `rule_source` and `immutable`, but we want to use the importing rule's calculated values ruleWithUpdates = { ...ruleWithUpdates, ...overrideFields }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/patch_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/patch_rule.ts index 113576e8d02e2..0b22ea39eaef4 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/patch_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/patch_rule.ts @@ -28,6 +28,7 @@ interface PatchRuleOptions { prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient; rulePatch: RulePatchProps; mlAuthz: MlAuthz; + isRuleCustomizationEnabled: boolean; } export const patchRule = async ({ @@ -36,6 +37,7 @@ export const patchRule = async ({ prebuiltRuleAssetClient, rulePatch, mlAuthz, + isRuleCustomizationEnabled, }: PatchRuleOptions): Promise => { const { rule_id: ruleId, id } = rulePatch; @@ -58,6 +60,7 @@ export const patchRule = async ({ prebuiltRuleAssetClient, existingRule, rulePatch, + isRuleCustomizationEnabled, }); const patchedInternalRule = await rulesClient.update({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/update_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/update_rule.ts index 8fd7f7a89dcb7..3d465d44fdc1d 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/update_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/update_rule.ts @@ -27,6 +27,7 @@ interface UpdateRuleArguments { prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient; ruleUpdate: RuleUpdateProps; mlAuthz: MlAuthz; + isRuleCustomizationEnabled: boolean; } export const updateRule = async ({ @@ -35,6 +36,7 @@ export const updateRule = async ({ prebuiltRuleAssetClient, ruleUpdate, mlAuthz, + isRuleCustomizationEnabled, }: UpdateRuleArguments): Promise => { const { rule_id: ruleId, id } = ruleUpdate; @@ -57,6 +59,7 @@ export const updateRule = async ({ prebuiltRuleAssetClient, existingRule, ruleUpdate, + isRuleCustomizationEnabled, }); const updatedRule = await rulesClient.update({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/upgrade_prebuilt_rule.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/upgrade_prebuilt_rule.ts index 64486bed14304..dff7c8a333ca7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/upgrade_prebuilt_rule.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/detection_rules_client/methods/upgrade_prebuilt_rule.ts @@ -25,12 +25,14 @@ export const upgradePrebuiltRule = async ({ ruleAsset, mlAuthz, prebuiltRuleAssetClient, + isRuleCustomizationEnabled, }: { actionsClient: ActionsClient; rulesClient: RulesClient; ruleAsset: PrebuiltRuleAsset; mlAuthz: MlAuthz; prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient; + isRuleCustomizationEnabled: boolean; }): Promise => { await validateMlAuth(mlAuthz, ruleAsset.type); @@ -73,6 +75,7 @@ export const upgradePrebuiltRule = async ({ prebuiltRuleAssetClient, existingRule, ruleUpdate: ruleAsset, + isRuleCustomizationEnabled, }); const updatedInternalRule = await rulesClient.update({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/calculate_rule_source_for_import.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/calculate_rule_source_for_import.test.ts index e3bfb75c6a88d..b1952ff6948c7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/calculate_rule_source_for_import.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/calculate_rule_source_for_import.test.ts @@ -15,6 +15,7 @@ describe('calculateRuleSourceForImport', () => { rule: getRulesSchemaMock(), prebuiltRuleAssetsByRuleId: {}, isKnownPrebuiltRule: false, + isRuleCustomizationEnabled: true, }); expect(result).toEqual({ @@ -33,6 +34,7 @@ describe('calculateRuleSourceForImport', () => { rule, prebuiltRuleAssetsByRuleId: {}, isKnownPrebuiltRule: true, + isRuleCustomizationEnabled: true, }); expect(result).toEqual({ @@ -53,6 +55,7 @@ describe('calculateRuleSourceForImport', () => { rule, prebuiltRuleAssetsByRuleId, isKnownPrebuiltRule: true, + isRuleCustomizationEnabled: true, }); expect(result).toEqual({ @@ -73,6 +76,7 @@ describe('calculateRuleSourceForImport', () => { rule, prebuiltRuleAssetsByRuleId, isKnownPrebuiltRule: true, + isRuleCustomizationEnabled: true, }); expect(result).toEqual({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/calculate_rule_source_for_import.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/calculate_rule_source_for_import.ts index 133566a7b776b..32a1f622b08d8 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/calculate_rule_source_for_import.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/calculate_rule_source_for_import.ts @@ -28,10 +28,12 @@ export const calculateRuleSourceForImport = ({ rule, prebuiltRuleAssetsByRuleId, isKnownPrebuiltRule, + isRuleCustomizationEnabled, }: { rule: ValidatedRuleToImport; prebuiltRuleAssetsByRuleId: Record; isKnownPrebuiltRule: boolean; + isRuleCustomizationEnabled: boolean; }): { ruleSource: RuleSource; immutable: boolean } => { const assetWithMatchingVersion = prebuiltRuleAssetsByRuleId[rule.rule_id]; // We convert here so that RuleSource calculation can @@ -43,6 +45,7 @@ export const calculateRuleSourceForImport = ({ rule: ruleResponseForImport, assetWithMatchingVersion, isKnownPrebuiltRule, + isRuleCustomizationEnabled, }); return { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/calculate_rule_source_from_asset.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/calculate_rule_source_from_asset.test.ts index 9a2f68479fdea..099c0f41f1720 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/calculate_rule_source_from_asset.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/calculate_rule_source_from_asset.test.ts @@ -15,6 +15,7 @@ describe('calculateRuleSourceFromAsset', () => { rule: getRulesSchemaMock(), assetWithMatchingVersion: undefined, isKnownPrebuiltRule: false, + isRuleCustomizationEnabled: true, }); expect(result).toEqual({ @@ -28,6 +29,7 @@ describe('calculateRuleSourceFromAsset', () => { rule: ruleToImport, assetWithMatchingVersion: undefined, isKnownPrebuiltRule: true, + isRuleCustomizationEnabled: true, }); expect(result).toEqual({ @@ -47,6 +49,7 @@ describe('calculateRuleSourceFromAsset', () => { // no other overwrites -> no differences }), isKnownPrebuiltRule: true, + isRuleCustomizationEnabled: true, }); expect(result).toEqual({ @@ -65,6 +68,7 @@ describe('calculateRuleSourceFromAsset', () => { name: 'Customized name', // mock a customization }), isKnownPrebuiltRule: true, + isRuleCustomizationEnabled: true, }); expect(result).toEqual({ diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/calculate_rule_source_from_asset.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/calculate_rule_source_from_asset.ts index 4f0caf9b10056..33063bedb11b2 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/calculate_rule_source_from_asset.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/calculate_rule_source_from_asset.ts @@ -24,10 +24,12 @@ export const calculateRuleSourceFromAsset = ({ rule, assetWithMatchingVersion, isKnownPrebuiltRule, + isRuleCustomizationEnabled, }: { rule: RuleResponse; assetWithMatchingVersion: PrebuiltRuleAsset | undefined; isKnownPrebuiltRule: boolean; + isRuleCustomizationEnabled: boolean; }): RuleSource => { if (!isKnownPrebuiltRule) { return { @@ -38,11 +40,15 @@ export const calculateRuleSourceFromAsset = ({ if (assetWithMatchingVersion == null) { return { type: 'external', - is_customized: true, + is_customized: isRuleCustomizationEnabled ? true : false, }; } - const isCustomized = calculateIsCustomized(assetWithMatchingVersion, rule); + const isCustomized = calculateIsCustomized({ + baseRule: assetWithMatchingVersion, + nextRule: rule, + isRuleCustomizationEnabled, + }); return { type: 'external', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/rule_source_importer/rule_source_importer.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/rule_source_importer/rule_source_importer.test.ts index 39c937f4645a7..426109bbeb3bb 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/rule_source_importer/rule_source_importer.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/rule_source_importer/rule_source_importer.test.ts @@ -138,6 +138,7 @@ describe('ruleSourceImporter', () => { rule, prebuiltRuleAssetsByRuleId: { 'rule-1': expect.objectContaining({ rule_id: 'rule-1' }) }, isKnownPrebuiltRule: true, + isRuleCustomizationEnabled: true, }); }); @@ -166,6 +167,7 @@ describe('ruleSourceImporter', () => { rule, prebuiltRuleAssetsByRuleId: { 'rule-1': expect.objectContaining({ rule_id: 'rule-1' }) }, isKnownPrebuiltRule: true, + isRuleCustomizationEnabled: true, }); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/rule_source_importer/rule_source_importer.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/rule_source_importer/rule_source_importer.ts index 1f5c2c5aa543b..0c553e6bb7c56 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/rule_source_importer/rule_source_importer.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/rule_source_importer/rule_source_importer.ts @@ -143,6 +143,8 @@ export class RuleSourceImporter implements IRuleSourceImporter { rule, prebuiltRuleAssetsByRuleId: this.matchingAssetsByRuleId, isKnownPrebuiltRule: this.availableRuleAssetIds.has(rule.rule_id), + isRuleCustomizationEnabled: + this.config.experimentalFeatures.prebuiltRulesCustomizationEnabled, }); } diff --git a/x-pack/plugins/security_solution/server/request_context_factory.ts b/x-pack/plugins/security_solution/server/request_context_factory.ts index 6b2718bac7704..c2b3782d405d0 100644 --- a/x-pack/plugins/security_solution/server/request_context_factory.ts +++ b/x-pack/plugins/security_solution/server/request_context_factory.ts @@ -152,6 +152,7 @@ export class RequestContextFactory implements IRequestContextFactory { actionsClient, savedObjectsClient: coreContext.savedObjects.client, mlAuthz, + isRuleCustomizationEnabled: config.experimentalFeatures.prebuiltRulesCustomizationEnabled, }); }), diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/configs/ess.config.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/configs/ess.config.ts new file mode 100644 index 0000000000000..2e1b278c9247f --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/configs/ess.config.ts @@ -0,0 +1,25 @@ +/* + * 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 { FtrConfigProviderContext } from '@kbn/test'; + +export default async function ({ readConfigFile }: FtrConfigProviderContext) { + const functionalConfig = await readConfigFile( + require.resolve('../../../../../../../config/ess/config.base.trial') + ); + + const testConfig = { + ...functionalConfig.getAll(), + testFiles: [require.resolve('..')], + junit: { + reportName: + 'Rules Management - Prebuilt Rule Customization Disabled Integration Tests - ESS Env', + }, + }; + + return testConfig; +} diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/configs/serverless.config.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/configs/serverless.config.ts new file mode 100644 index 0000000000000..462a971d8dda7 --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/configs/serverless.config.ts @@ -0,0 +1,17 @@ +/* + * 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 { createTestConfig } from '../../../../../../../config/serverless/config.base'; + +export default createTestConfig({ + testFiles: [require.resolve('..')], + junit: { + reportName: + 'Rules Management - Prebuilt Rule Customization Disabled Integration Tests - Serverless Env', + }, + kbnTestServerArgs: [], +}); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/index.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/index.ts new file mode 100644 index 0000000000000..0d78605426dbf --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/index.ts @@ -0,0 +1,14 @@ +/* + * 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 { FtrProviderContext } from '../../../../../../ftr_provider_context'; + +export default ({ loadTestFile }: FtrProviderContext): void => { + describe('Rules Management - Prebuilt Rules - Prebuilt Rule Customization Disabled', function () { + loadTestFile(require.resolve('./is_customized_calculation')); + }); +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/is_customized_calculation.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/is_customized_calculation.ts new file mode 100644 index 0000000000000..e4ae135b481ed --- /dev/null +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/is_customized_calculation.ts @@ -0,0 +1,218 @@ +/* + * 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 expect from 'expect'; +import { + BulkActionEditTypeEnum, + BulkActionTypeEnum, +} from '@kbn/security-solution-plugin/common/api/detection_engine'; +import { deleteAllRules } from '../../../../../../../common/utils/security_solution'; +import { FtrProviderContext } from '../../../../../../ftr_provider_context'; +import { + deleteAllPrebuiltRuleAssets, + createRuleAssetSavedObject, + createPrebuiltRuleAssetSavedObjects, + installPrebuiltRules, +} from '../../../../utils'; + +export default ({ getService }: FtrProviderContext) => { + const supertest = getService('supertest'); + const securitySolutionApi = getService('securitySolutionApi'); + const log = getService('log'); + const es = getService('es'); + + const ruleAsset = createRuleAssetSavedObject({ + rule_id: 'test-rule-id', + }); + + describe('@ess @serverless @skipInServerlessMKI is_customized calculation with disabled customization', () => { + beforeEach(async () => { + await deleteAllRules(supertest, log); + await deleteAllPrebuiltRuleAssets(es, log); + }); + + it('should set is_customized to "false" on prebuilt rule PATCH', async () => { + await createPrebuiltRuleAssetSavedObjects(es, [ruleAsset]); + await installPrebuiltRules(es, supertest); + + const { body: findResult } = await securitySolutionApi + .findRules({ + query: { + per_page: 1, + filter: `alert.attributes.params.immutable: true`, + }, + }) + .expect(200); + const prebuiltRule = findResult.data[0]; + + // Check that the rule has been created and is not customized + expect(prebuiltRule).not.toBeNull(); + expect(prebuiltRule.rule_source.is_customized).toEqual(false); + + const { body } = await securitySolutionApi + .patchRule({ + body: { + rule_id: 'test-rule-id', + name: 'some other rule name', + }, + }) + .expect(200); + + // Check that the rule name has been updated and the rule is still not customized + expect(body).toEqual( + expect.objectContaining({ + name: 'some other rule name', + }) + ); + expect(body.rule_source.is_customized).toEqual(false); + }); + + it('should set is_customized to "false" on prebuilt rule UPDATE', async () => { + await createPrebuiltRuleAssetSavedObjects(es, [ruleAsset]); + await installPrebuiltRules(es, supertest); + + const { body: findResult } = await securitySolutionApi + .findRules({ + query: { + per_page: 1, + filter: `alert.attributes.params.immutable: true`, + }, + }) + .expect(200); + const prebuiltRule = findResult.data[0]; + + // Check that the rule has been created and is not customized + expect(prebuiltRule).not.toBeNull(); + expect(prebuiltRule.rule_source.is_customized).toEqual(false); + + const { body } = await securitySolutionApi + .updateRule({ + body: { + ...prebuiltRule, + id: undefined, // id together with rule_id is not allowed + name: 'some other rule name', + }, + }) + .expect(200); + + // Check that the rule name has been updated and the rule is still not customized + expect(body).toEqual( + expect.objectContaining({ + name: 'some other rule name', + }) + ); + expect(body.rule_source.is_customized).toEqual(false); + }); + + it('should not allow prebuilt rule customization on import', async () => { + await createPrebuiltRuleAssetSavedObjects(es, [ruleAsset]); + await installPrebuiltRules(es, supertest); + + const { body: findResult } = await securitySolutionApi + .findRules({ + query: { + per_page: 1, + filter: `alert.attributes.params.immutable: true`, + }, + }) + .expect(200); + const prebuiltRule = findResult.data[0]; + + // Check that the rule has been created and is not customized + expect(prebuiltRule).not.toBeNull(); + expect(prebuiltRule.rule_source.is_customized).toEqual(false); + + const ruleBuffer = Buffer.from( + JSON.stringify({ + ...prebuiltRule, + name: 'some other rule name', + }) + ); + + const { body } = await securitySolutionApi + .importRules({ query: {} }) + .attach('file', ruleBuffer, 'rules.ndjson') + .expect('Content-Type', 'application/json; charset=utf-8') + .expect(200); + + expect(body).toMatchObject({ + rules_count: 1, + success: false, + success_count: 0, + errors: [ + { + error: { + message: expect.stringContaining('Importing prebuilt rules is not supported'), + }, + rule_id: 'test-rule-id', + }, + ], + }); + + // Check that the rule has not been customized + const { body: importedRule } = await securitySolutionApi.readRule({ + query: { rule_id: prebuiltRule.rule_id }, + }); + expect(importedRule.rule_source.is_customized).toEqual(false); + }); + + it('should not allow rule customization on bulk edit', async () => { + await createPrebuiltRuleAssetSavedObjects(es, [ruleAsset]); + await installPrebuiltRules(es, supertest); + + const { body: findResult } = await securitySolutionApi + .findRules({ + query: { + per_page: 1, + filter: `alert.attributes.params.immutable: true`, + }, + }) + .expect(200); + const prebuiltRule = findResult.data[0]; + + // Check that the rule has been created and is not customized + expect(prebuiltRule).not.toBeNull(); + expect(prebuiltRule.rule_source.is_customized).toEqual(false); + + const { body: bulkResult } = await securitySolutionApi + .performRulesBulkAction({ + query: {}, + body: { + ids: [prebuiltRule.id], + action: BulkActionTypeEnum.edit, + [BulkActionTypeEnum.edit]: [ + { + type: BulkActionEditTypeEnum.add_tags, + value: ['test'], + }, + ], + }, + }) + .expect(500); + + expect(bulkResult).toMatchObject( + expect.objectContaining({ + attributes: expect.objectContaining({ + summary: { + failed: 1, + skipped: 0, + succeeded: 0, + total: 1, + }, + errors: [expect.objectContaining({ message: "Elastic rule can't be edited" })], + }), + }) + ); + + // Check that the rule has not been customized + const { body: ruleAfterUpdate } = await securitySolutionApi.readRule({ + query: { rule_id: prebuiltRule.rule_id }, + }); + expect(ruleAfterUpdate.rule_source.is_customized).toEqual(false); + }); + }); +}; diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/trial_license_complete_tier/configs/ess.config.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/configs/ess.config.ts similarity index 91% rename from x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/trial_license_complete_tier/configs/ess.config.ts rename to x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/configs/ess.config.ts index eee14323c9b98..ff46ebb70d7c8 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/trial_license_complete_tier/configs/ess.config.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/configs/ess.config.ts @@ -17,7 +17,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { testFiles: [require.resolve('..')], junit: { reportName: - 'Rules Management - Prebuilt Rule Customization Integration Tests - ESS Env - Trial License', + 'Rules Management - Prebuilt Rule Customization Enabled Integration Tests - ESS Env', }, }; testConfig.kbnTestServer.serverArgs = testConfig.kbnTestServer.serverArgs.map((arg: string) => { diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/trial_license_complete_tier/configs/serverless.config.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/configs/serverless.config.ts similarity index 84% rename from x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/trial_license_complete_tier/configs/serverless.config.ts rename to x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/configs/serverless.config.ts index 79a23c85d2279..8c1d777665667 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/trial_license_complete_tier/configs/serverless.config.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/configs/serverless.config.ts @@ -11,7 +11,7 @@ export default createTestConfig({ testFiles: [require.resolve('..')], junit: { reportName: - 'Rules Management - Prebuilt Rule Customization Integration Tests - Serverless Env - Complete Tier', + 'Rules Management - Prebuilt Rule Customization Enabled Integration Tests - Serverless Env', }, kbnTestServerArgs: [ `--xpack.securitySolution.enableExperimental=${JSON.stringify([ diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/trial_license_complete_tier/import_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/import_rules.ts similarity index 100% rename from x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/trial_license_complete_tier/import_rules.ts rename to x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/import_rules.ts diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/trial_license_complete_tier/index.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/index.ts similarity index 94% rename from x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/trial_license_complete_tier/index.ts rename to x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/index.ts index 58904243e51ca..d89f8ed5d49d3 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/trial_license_complete_tier/index.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/index.ts @@ -8,7 +8,7 @@ import { FtrProviderContext } from '../../../../../../ftr_provider_context'; export default ({ loadTestFile }: FtrProviderContext): void => { - describe('Rules Management - Prebuilt Rules - Prebuilt Rule Customization', function () { + describe('Rules Management - Prebuilt Rules - Prebuilt Rule Customization Enabled', function () { loadTestFile(require.resolve('./is_customized_calculation')); loadTestFile(require.resolve('./import_rules')); loadTestFile(require.resolve('./rules_export')); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/trial_license_complete_tier/is_customized_calculation.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/is_customized_calculation.ts similarity index 100% rename from x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/trial_license_complete_tier/is_customized_calculation.ts rename to x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/is_customized_calculation.ts diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/trial_license_complete_tier/rules_export.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/rules_export.ts similarity index 100% rename from x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/trial_license_complete_tier/rules_export.ts rename to x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/rules_export.ts diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules.ts index 86dde0735424e..a47f84ef930ad 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_patch/basic_license_essentials_tier/patch_rules.ts @@ -7,25 +7,21 @@ import expect from 'expect'; +import { createRule, deleteAllRules } from '../../../../../../common/utils/security_solution'; import { FtrProviderContext } from '../../../../../ftr_provider_context'; import { + createHistoricalPrebuiltRuleAssetSavedObjects, + createRuleAssetSavedObject, + deleteAllPrebuiltRuleAssets, + getCustomQueryRuleParams, getSimpleRule, getSimpleRuleOutput, - getCustomQueryRuleParams, + getSimpleRuleOutputWithoutRuleId, + installPrebuiltRules, removeServerGeneratedProperties, removeServerGeneratedPropertiesIncludingRuleId, - getSimpleRuleOutputWithoutRuleId, updateUsername, - createHistoricalPrebuiltRuleAssetSavedObjects, - installPrebuiltRules, - createRuleAssetSavedObject, } from '../../../utils'; -import { - createAlertsIndex, - deleteAllRules, - createRule, - deleteAllAlerts, -} from '../../../../../../common/utils/security_solution'; export default ({ getService }: FtrProviderContext) => { const supertest = getService('supertest'); @@ -37,12 +33,8 @@ export default ({ getService }: FtrProviderContext) => { describe('@ess @serverless @serverlessQA patch_rules', () => { describe('patch rules', () => { beforeEach(async () => { - await createAlertsIndex(supertest, log); - }); - - afterEach(async () => { - await deleteAllAlerts(supertest, log, es); await deleteAllRules(supertest, log); + await deleteAllPrebuiltRuleAssets(es, log); }); it('should patch a single rule property of name using a rule_id', async () => { @@ -262,10 +254,6 @@ export default ({ getService }: FtrProviderContext) => { }); describe('max signals', () => { - afterEach(async () => { - await deleteAllRules(supertest, log); - }); - it('does NOT patch a rule when max_signals is less than 1', async () => { await securitySolutionApi.createRule({ body: getCustomQueryRuleParams({ rule_id: 'rule-1', max_signals: 100 }),