mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[8.16] [Security Solution] Skip isCustomized calculation when the feature flag is off (#201825) (#202751)
# Backport This will backport the following commits from `main` to `8.16`: - [[Security Solution] Skip isCustomized calculation when the feature flag is off (#201825)](https://github.com/elastic/kibana/pull/201825) <!--- Backport version: 8.9.8 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Dmitrii Shevchenko","email":"dmitrii.shevchenko@elastic.co"},"sourceCommit":{"committedDate":"2024-12-03T12:11:24Z","message":"[Security Solution] Skip isCustomized calculation when the feature flag is off (#201825)\n\n**Resolves: https://github.com/elastic/kibana/issues/201632**\r\n\r\n## Summary \r\n\r\nWhen the rule customization feature flag is disabled, we should always\r\nreturn `isCustomized: false`, regardless of any changes introduced to a\r\nrule. This ensures that we do not accidentally mark prebuilt rules as\r\ncustomized in 8.16 with the feature flag off. For more details, refer to\r\nthe related issue: https://github.com/elastic/kibana/issues/201632\r\n\r\n### Main Changes \r\n\r\n- The primary change in this PR is encapsulated in the\r\n`calculateIsCustomized` function\r\n- Other changes involve passing the feature flag to this function\r\n- Added integration tests to cover all API CRUD operations that can be\r\nperformed with rules","sha":"22911c1828f40160cf3a2935300aec18c11b56e9","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["bug","release_note:skip","v9.0.0","Team:Detections and Resp","Team: SecuritySolution","Team:Detection Rule Management","Feature:Prebuilt Detection Rules","backport:version","v8.17.0","v8.18.0","v8.16.2"],"number":201825,"url":"https://github.com/elastic/kibana/pull/201825","mergeCommit":{"message":"[Security Solution] Skip isCustomized calculation when the feature flag is off (#201825)\n\n**Resolves: https://github.com/elastic/kibana/issues/201632**\r\n\r\n## Summary \r\n\r\nWhen the rule customization feature flag is disabled, we should always\r\nreturn `isCustomized: false`, regardless of any changes introduced to a\r\nrule. This ensures that we do not accidentally mark prebuilt rules as\r\ncustomized in 8.16 with the feature flag off. For more details, refer to\r\nthe related issue: https://github.com/elastic/kibana/issues/201632\r\n\r\n### Main Changes \r\n\r\n- The primary change in this PR is encapsulated in the\r\n`calculateIsCustomized` function\r\n- Other changes involve passing the feature flag to this function\r\n- Added integration tests to cover all API CRUD operations that can be\r\nperformed with rules","sha":"22911c1828f40160cf3a2935300aec18c11b56e9"}},"sourceBranch":"main","suggestedTargetBranches":["8.16"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","labelRegex":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/201825","number":201825,"mergeCommit":{"message":"[Security Solution] Skip isCustomized calculation when the feature flag is off (#201825)\n\n**Resolves: https://github.com/elastic/kibana/issues/201632**\r\n\r\n## Summary \r\n\r\nWhen the rule customization feature flag is disabled, we should always\r\nreturn `isCustomized: false`, regardless of any changes introduced to a\r\nrule. This ensures that we do not accidentally mark prebuilt rules as\r\ncustomized in 8.16 with the feature flag off. For more details, refer to\r\nthe related issue: https://github.com/elastic/kibana/issues/201632\r\n\r\n### Main Changes \r\n\r\n- The primary change in this PR is encapsulated in the\r\n`calculateIsCustomized` function\r\n- Other changes involve passing the feature flag to this function\r\n- Added integration tests to cover all API CRUD operations that can be\r\nperformed with rules","sha":"22911c1828f40160cf3a2935300aec18c11b56e9"}},{"branch":"8.17","label":"v8.17.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"url":"https://github.com/elastic/kibana/pull/202696","number":202696,"state":"MERGED","mergeCommit":{"sha":"487149e077e61341f81c871b74f36a9e20df67c0","message":"[8.17] [Security Solution] Skip isCustomized calculation when the feature flag is off (#201825) (#202696)\n\n# Backport\n\nThis will backport the following commits from `main` to `8.17`:\n- [[Security Solution] Skip isCustomized calculation when the feature\nflag is off (#201825)](https://github.com/elastic/kibana/pull/201825)\n\n<!--- Backport version: 9.4.3 -->\n\n### Questions ?\nPlease refer to the [Backport tool\ndocumentation](https://github.com/sqren/backport)\n\n<!--BACKPORT [{\"author\":{\"name\":\"Dmitrii\nShevchenko\",\"email\":\"dmitrii.shevchenko@elastic.co\"},\"sourceCommit\":{\"committedDate\":\"2024-12-03T12:11:24Z\",\"message\":\"[Security\nSolution] Skip isCustomized calculation when the feature flag is off\n(#201825)\\n\\n**Resolves:\nhttps://github.com/elastic/kibana/issues/201632**\\r\\n\\r\\n## Summary\n\\r\\n\\r\\nWhen the rule customization feature flag is disabled, we should\nalways\\r\\nreturn `isCustomized: false`, regardless of any changes\nintroduced to a\\r\\nrule. This ensures that we do not accidentally mark\nprebuilt rules as\\r\\ncustomized in 8.16 with the feature flag off. For\nmore details, refer to\\r\\nthe related issue:\nhttps://github.com/elastic/kibana/issues/201632\\r\\n\\r\\n### Main Changes\n\\r\\n\\r\\n- The primary change in this PR is encapsulated in\nthe\\r\\n`calculateIsCustomized` function\\r\\n- Other changes involve\npassing the feature flag to this function\\r\\n- Added integration tests\nto cover all API CRUD operations that can be\\r\\nperformed with\nrules\",\"sha\":\"22911c1828f40160cf3a2935300aec18c11b56e9\",\"branchLabelMapping\":{\"^v9.0.0$\":\"main\",\"^v8.18.0$\":\"8.x\",\"^v(\\\\d+).(\\\\d+).\\\\d+$\":\"$1.$2\"}},\"sourcePullRequest\":{\"labels\":[\"bug\",\"release_note:skip\",\"v9.0.0\",\"Team:Detections\nand Resp\",\"Team: SecuritySolution\",\"Team:Detection Rule\nManagement\",\"Feature:Prebuilt Detection\nRules\",\"backport:version\",\"v8.17.0\",\"v8.18.0\",\"v8.16.2\"],\"title\":\"[Security\nSolution] Skip isCustomized calculation when the feature flag is\noff\",\"number\":201825,\"url\":\"https://github.com/elastic/kibana/pull/201825\",\"mergeCommit\":{\"message\":\"[Security\nSolution] Skip isCustomized calculation when the feature flag is off\n(#201825)\\n\\n**Resolves:\nhttps://github.com/elastic/kibana/issues/201632**\\r\\n\\r\\n## Summary\n\\r\\n\\r\\nWhen the rule customization feature flag is disabled, we should\nalways\\r\\nreturn `isCustomized: false`, regardless of any changes\nintroduced to a\\r\\nrule. This ensures that we do not accidentally mark\nprebuilt rules as\\r\\ncustomized in 8.16 with the feature flag off. For\nmore details, refer to\\r\\nthe related issue:\nhttps://github.com/elastic/kibana/issues/201632\\r\\n\\r\\n### Main Changes\n\\r\\n\\r\\n- The primary change in this PR is encapsulated in\nthe\\r\\n`calculateIsCustomized` function\\r\\n- Other changes involve\npassing the feature flag to this function\\r\\n- Added integration tests\nto cover all API CRUD operations that can be\\r\\nperformed with\nrules\",\"sha\":\"22911c1828f40160cf3a2935300aec18c11b56e9\"}},\"sourceBranch\":\"main\",\"suggestedTargetBranches\":[\"8.17\",\"8.x\",\"8.16\"],\"targetPullRequestStates\":[{\"branch\":\"main\",\"label\":\"v9.0.0\",\"branchLabelMappingKey\":\"^v9.0.0$\",\"isSourceBranch\":true,\"state\":\"MERGED\",\"url\":\"https://github.com/elastic/kibana/pull/201825\",\"number\":201825,\"mergeCommit\":{\"message\":\"[Security\nSolution] Skip isCustomized calculation when the feature flag is off\n(#201825)\\n\\n**Resolves:\nhttps://github.com/elastic/kibana/issues/201632**\\r\\n\\r\\n## Summary\n\\r\\n\\r\\nWhen the rule customization feature flag is disabled, we should\nalways\\r\\nreturn `isCustomized: false`, regardless of any changes\nintroduced to a\\r\\nrule. This ensures that we do not accidentally mark\nprebuilt rules as\\r\\ncustomized in 8.16 with the feature flag off. For\nmore details, refer to\\r\\nthe related issue:\nhttps://github.com/elastic/kibana/issues/201632\\r\\n\\r\\n### Main Changes\n\\r\\n\\r\\n- The primary change in this PR is encapsulated in\nthe\\r\\n`calculateIsCustomized` function\\r\\n- Other changes involve\npassing the feature flag to this function\\r\\n- Added integration tests\nto cover all API CRUD operations that can be\\r\\nperformed with\nrules\",\"sha\":\"22911c1828f40160cf3a2935300aec18c11b56e9\"}},{\"branch\":\"8.17\",\"label\":\"v8.17.0\",\"branchLabelMappingKey\":\"^v(\\\\d+).(\\\\d+).\\\\d+$\",\"isSourceBranch\":false,\"state\":\"NOT_CREATED\"},{\"branch\":\"8.x\",\"label\":\"v8.18.0\",\"branchLabelMappingKey\":\"^v8.18.0$\",\"isSourceBranch\":false,\"state\":\"NOT_CREATED\"},{\"branch\":\"8.16\",\"label\":\"v8.16.2\",\"branchLabelMappingKey\":\"^v(\\\\d+).(\\\\d+).\\\\d+$\",\"isSourceBranch\":false,\"state\":\"NOT_CREATED\"}]}]\nBACKPORT-->\n\nCo-authored-by: Dmitrii Shevchenko <dmitrii.shevchenko@elastic.co>"}},{"branch":"8.x","label":"v8.18.0","labelRegex":"^v8.18.0$","isSourceBranch":false,"url":"https://github.com/elastic/kibana/pull/202697","number":202697,"state":"MERGED","mergeCommit":{"sha":"8c9181aa48796a8467e38ad1431238ebaa78de7e","message":"[8.x] [Security Solution] Skip isCustomized calculation when the feature flag is off (#201825) (#202697)\n\n# Backport\n\nThis will backport the following commits from `main` to `8.x`:\n- [[Security Solution] Skip isCustomized calculation when the feature\nflag is off (#201825)](https://github.com/elastic/kibana/pull/201825)\n\n<!--- Backport version: 9.4.3 -->\n\n### Questions ?\nPlease refer to the [Backport tool\ndocumentation](https://github.com/sqren/backport)\n\n<!--BACKPORT [{\"author\":{\"name\":\"Dmitrii\nShevchenko\",\"email\":\"dmitrii.shevchenko@elastic.co\"},\"sourceCommit\":{\"committedDate\":\"2024-12-03T12:11:24Z\",\"message\":\"[Security\nSolution] Skip isCustomized calculation when the feature flag is off\n(#201825)\\n\\n**Resolves:\nhttps://github.com/elastic/kibana/issues/201632**\\r\\n\\r\\n## Summary\n\\r\\n\\r\\nWhen the rule customization feature flag is disabled, we should\nalways\\r\\nreturn `isCustomized: false`, regardless of any changes\nintroduced to a\\r\\nrule. This ensures that we do not accidentally mark\nprebuilt rules as\\r\\ncustomized in 8.16 with the feature flag off. For\nmore details, refer to\\r\\nthe related issue:\nhttps://github.com/elastic/kibana/issues/201632\\r\\n\\r\\n### Main Changes\n\\r\\n\\r\\n- The primary change in this PR is encapsulated in\nthe\\r\\n`calculateIsCustomized` function\\r\\n- Other changes involve\npassing the feature flag to this function\\r\\n- Added integration tests\nto cover all API CRUD operations that can be\\r\\nperformed with\nrules\",\"sha\":\"22911c1828f40160cf3a2935300aec18c11b56e9\",\"branchLabelMapping\":{\"^v9.0.0$\":\"main\",\"^v8.18.0$\":\"8.x\",\"^v(\\\\d+).(\\\\d+).\\\\d+$\":\"$1.$2\"}},\"sourcePullRequest\":{\"labels\":[\"bug\",\"release_note:skip\",\"v9.0.0\",\"Team:Detections\nand Resp\",\"Team: SecuritySolution\",\"Team:Detection Rule\nManagement\",\"Feature:Prebuilt Detection\nRules\",\"backport:version\",\"v8.17.0\",\"v8.18.0\",\"v8.16.2\"],\"title\":\"[Security\nSolution] Skip isCustomized calculation when the feature flag is\noff\",\"number\":201825,\"url\":\"https://github.com/elastic/kibana/pull/201825\",\"mergeCommit\":{\"message\":\"[Security\nSolution] Skip isCustomized calculation when the feature flag is off\n(#201825)\\n\\n**Resolves:\nhttps://github.com/elastic/kibana/issues/201632**\\r\\n\\r\\n## Summary\n\\r\\n\\r\\nWhen the rule customization feature flag is disabled, we should\nalways\\r\\nreturn `isCustomized: false`, regardless of any changes\nintroduced to a\\r\\nrule. This ensures that we do not accidentally mark\nprebuilt rules as\\r\\ncustomized in 8.16 with the feature flag off. For\nmore details, refer to\\r\\nthe related issue:\nhttps://github.com/elastic/kibana/issues/201632\\r\\n\\r\\n### Main Changes\n\\r\\n\\r\\n- The primary change in this PR is encapsulated in\nthe\\r\\n`calculateIsCustomized` function\\r\\n- Other changes involve\npassing the feature flag to this function\\r\\n- Added integration tests\nto cover all API CRUD operations that can be\\r\\nperformed with\nrules\",\"sha\":\"22911c1828f40160cf3a2935300aec18c11b56e9\"}},\"sourceBranch\":\"main\",\"suggestedTargetBranches\":[\"8.17\",\"8.x\",\"8.16\"],\"targetPullRequestStates\":[{\"branch\":\"main\",\"label\":\"v9.0.0\",\"branchLabelMappingKey\":\"^v9.0.0$\",\"isSourceBranch\":true,\"state\":\"MERGED\",\"url\":\"https://github.com/elastic/kibana/pull/201825\",\"number\":201825,\"mergeCommit\":{\"message\":\"[Security\nSolution] Skip isCustomized calculation when the feature flag is off\n(#201825)\\n\\n**Resolves:\nhttps://github.com/elastic/kibana/issues/201632**\\r\\n\\r\\n## Summary\n\\r\\n\\r\\nWhen the rule customization feature flag is disabled, we should\nalways\\r\\nreturn `isCustomized: false`, regardless of any changes\nintroduced to a\\r\\nrule. This ensures that we do not accidentally mark\nprebuilt rules as\\r\\ncustomized in 8.16 with the feature flag off. For\nmore details, refer to\\r\\nthe related issue:\nhttps://github.com/elastic/kibana/issues/201632\\r\\n\\r\\n### Main Changes\n\\r\\n\\r\\n- The primary change in this PR is encapsulated in\nthe\\r\\n`calculateIsCustomized` function\\r\\n- Other changes involve\npassing the feature flag to this function\\r\\n- Added integration tests\nto cover all API CRUD operations that can be\\r\\nperformed with\nrules\",\"sha\":\"22911c1828f40160cf3a2935300aec18c11b56e9\"}},{\"branch\":\"8.17\",\"label\":\"v8.17.0\",\"branchLabelMappingKey\":\"^v(\\\\d+).(\\\\d+).\\\\d+$\",\"isSourceBranch\":false,\"state\":\"NOT_CREATED\"},{\"branch\":\"8.x\",\"label\":\"v8.18.0\",\"branchLabelMappingKey\":\"^v8.18.0$\",\"isSourceBranch\":false,\"state\":\"NOT_CREATED\"},{\"branch\":\"8.16\",\"label\":\"v8.16.2\",\"branchLabelMappingKey\":\"^v(\\\\d+).(\\\\d+).\\\\d+$\",\"isSourceBranch\":false,\"state\":\"NOT_CREATED\"}]}]\nBACKPORT-->\n\nCo-authored-by: Dmitrii Shevchenko <dmitrii.shevchenko@elastic.co>"}},{"branch":"8.16","label":"v8.16.2","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}] BACKPORT-->
This commit is contained in:
parent
e21bfd6186
commit
e2eaf0c7d2
33 changed files with 586 additions and 34 deletions
|
@ -60,7 +60,8 @@ disabled:
|
|||
- 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
|
||||
|
|
|
@ -48,7 +48,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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -51,6 +51,7 @@ describe('DetectionRulesClient.createCustomRule', () => {
|
|||
rulesClient,
|
||||
mlAuthz,
|
||||
savedObjectsClient,
|
||||
isRuleCustomizationEnabled: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ describe('DetectionRulesClient.createPrebuiltRule', () => {
|
|||
rulesClient,
|
||||
mlAuthz,
|
||||
savedObjectsClient,
|
||||
isRuleCustomizationEnabled: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ describe('DetectionRulesClient.deleteRule', () => {
|
|||
rulesClient,
|
||||
mlAuthz,
|
||||
savedObjectsClient,
|
||||
isRuleCustomizationEnabled: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ describe('DetectionRulesClient.importRule', () => {
|
|||
rulesClient,
|
||||
mlAuthz,
|
||||
savedObjectsClient,
|
||||
isRuleCustomizationEnabled: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ describe('DetectionRulesClient.patchRule', () => {
|
|||
rulesClient,
|
||||
mlAuthz,
|
||||
savedObjectsClient,
|
||||
isRuleCustomizationEnabled: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ interface DetectionRulesClientParams {
|
|||
rulesClient: RulesClient;
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
mlAuthz: MlAuthz;
|
||||
isRuleCustomizationEnabled: boolean;
|
||||
}
|
||||
|
||||
export const createDetectionRulesClient = ({
|
||||
|
@ -42,6 +43,7 @@ export const createDetectionRulesClient = ({
|
|||
rulesClient,
|
||||
mlAuthz,
|
||||
savedObjectsClient,
|
||||
isRuleCustomizationEnabled,
|
||||
}: DetectionRulesClientParams): IDetectionRulesClient => {
|
||||
const prebuiltRuleAssetClient = createPrebuiltRuleAssetsClient(savedObjectsClient);
|
||||
|
||||
|
@ -86,6 +88,7 @@ export const createDetectionRulesClient = ({
|
|||
prebuiltRuleAssetClient,
|
||||
mlAuthz,
|
||||
ruleUpdate,
|
||||
isRuleCustomizationEnabled,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -98,6 +101,7 @@ export const createDetectionRulesClient = ({
|
|||
prebuiltRuleAssetClient,
|
||||
mlAuthz,
|
||||
rulePatch,
|
||||
isRuleCustomizationEnabled,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -116,6 +120,7 @@ export const createDetectionRulesClient = ({
|
|||
ruleAsset,
|
||||
mlAuthz,
|
||||
prebuiltRuleAssetClient,
|
||||
isRuleCustomizationEnabled,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -128,6 +133,7 @@ export const createDetectionRulesClient = ({
|
|||
importRulePayload: args,
|
||||
mlAuthz,
|
||||
prebuiltRuleAssetClient,
|
||||
isRuleCustomizationEnabled,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
|
|
@ -50,6 +50,7 @@ describe('DetectionRulesClient.updateRule', () => {
|
|||
rulesClient,
|
||||
mlAuthz,
|
||||
savedObjectsClient,
|
||||
isRuleCustomizationEnabled: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ describe('DetectionRulesClient.upgradePrebuiltRule', () => {
|
|||
rulesClient,
|
||||
mlAuthz,
|
||||
savedObjectsClient,
|
||||
isRuleCustomizationEnabled: true,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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<RuleResponse> => {
|
||||
const typeSpecificParams = patchTypeSpecificParams(rulePatch, existingRule);
|
||||
|
||||
|
@ -122,6 +124,7 @@ export const applyRulePatch = async ({
|
|||
nextRule.rule_source = await calculateRuleSource({
|
||||
rule: nextRule,
|
||||
prebuiltRuleAssetClient,
|
||||
isRuleCustomizationEnabled,
|
||||
});
|
||||
|
||||
return nextRule;
|
||||
|
|
|
@ -17,12 +17,14 @@ interface ApplyRuleUpdateProps {
|
|||
prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient;
|
||||
existingRule: RuleResponse;
|
||||
ruleUpdate: RuleUpdateProps;
|
||||
isRuleCustomizationEnabled: boolean;
|
||||
}
|
||||
|
||||
export const applyRuleUpdate = async ({
|
||||
prebuiltRuleAssetClient,
|
||||
existingRule,
|
||||
ruleUpdate,
|
||||
isRuleCustomizationEnabled,
|
||||
}: ApplyRuleUpdateProps): Promise<RuleResponse> => {
|
||||
const nextRule: RuleResponse = {
|
||||
...applyRuleDefaults(ruleUpdate),
|
||||
|
@ -46,6 +48,7 @@ export const applyRuleUpdate = async ({
|
|||
nextRule.rule_source = await calculateRuleSource({
|
||||
rule: nextRule,
|
||||
prebuiltRuleAssetClient,
|
||||
isRuleCustomizationEnabled,
|
||||
});
|
||||
|
||||
return nextRule;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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<RuleSource> {
|
||||
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',
|
||||
|
|
|
@ -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<RuleResponse> => {
|
||||
const { ruleToImport, overwriteRules, allowMissingConnectorSecrets } = importRulePayload;
|
||||
|
||||
|
@ -57,6 +59,7 @@ export const importRule = async ({
|
|||
prebuiltRuleAssetClient,
|
||||
existingRule,
|
||||
ruleUpdate: ruleToImport,
|
||||
isRuleCustomizationEnabled,
|
||||
});
|
||||
|
||||
const updatedRule = await rulesClient.update({
|
||||
|
|
|
@ -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<RuleResponse> => {
|
||||
const { rule_id: ruleId, id } = rulePatch;
|
||||
|
||||
|
@ -58,6 +60,7 @@ export const patchRule = async ({
|
|||
prebuiltRuleAssetClient,
|
||||
existingRule,
|
||||
rulePatch,
|
||||
isRuleCustomizationEnabled,
|
||||
});
|
||||
|
||||
const patchedInternalRule = await rulesClient.update({
|
||||
|
|
|
@ -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<RuleResponse> => {
|
||||
const { rule_id: ruleId, id } = ruleUpdate;
|
||||
|
||||
|
@ -57,6 +59,7 @@ export const updateRule = async ({
|
|||
prebuiltRuleAssetClient,
|
||||
existingRule,
|
||||
ruleUpdate,
|
||||
isRuleCustomizationEnabled,
|
||||
});
|
||||
|
||||
const updatedRule = await rulesClient.update({
|
||||
|
|
|
@ -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<RuleResponse> => {
|
||||
await validateMlAuth(mlAuthz, ruleAsset.type);
|
||||
|
||||
|
@ -73,6 +75,7 @@ export const upgradePrebuiltRule = async ({
|
|||
prebuiltRuleAssetClient,
|
||||
existingRule,
|
||||
ruleUpdate: ruleAsset,
|
||||
isRuleCustomizationEnabled,
|
||||
});
|
||||
|
||||
const updatedInternalRule = await rulesClient.update({
|
||||
|
|
|
@ -139,6 +139,7 @@ export class RequestContextFactory implements IRequestContextFactory {
|
|||
rulesClient: startPlugins.alerting.getRulesClientWithRequest(request),
|
||||
savedObjectsClient: coreContext.savedObjects.client,
|
||||
mlAuthz,
|
||||
isRuleCustomizationEnabled: config.experimentalFeatures.prebuiltRulesCustomizationEnabled,
|
||||
});
|
||||
}),
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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: [],
|
||||
});
|
|
@ -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'));
|
||||
});
|
||||
};
|
|
@ -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('immutable: Invalid literal value, expected false'),
|
||||
},
|
||||
rule_id: '(unknown 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);
|
||||
});
|
||||
});
|
||||
};
|
|
@ -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) => {
|
|
@ -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([
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* 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 {
|
||||
SAMPLE_PREBUILT_RULES_WITH_HISTORICAL_VERSIONS,
|
||||
combineArrayToNdJson,
|
||||
createHistoricalPrebuiltRuleAssetSavedObjects,
|
||||
deleteAllPrebuiltRuleAssets,
|
||||
fetchRule,
|
||||
getCustomQueryRuleParams,
|
||||
getInstalledRules,
|
||||
} from '../../../../utils';
|
||||
import { deleteAllRules } from '../../../../../../../common/utils/security_solution';
|
||||
import { FtrProviderContext } from '../../../../../../ftr_provider_context';
|
||||
|
||||
export default ({ getService }: FtrProviderContext): void => {
|
||||
const supertest = getService('supertest');
|
||||
const es = getService('es');
|
||||
const log = getService('log');
|
||||
const securitySolutionApi = getService('securitySolutionApi');
|
||||
|
||||
const importRules = async (rules: unknown[]) => {
|
||||
const buffer = Buffer.from(combineArrayToNdJson(rules));
|
||||
|
||||
return securitySolutionApi
|
||||
.importRules({ query: {} })
|
||||
.attach('file', buffer, 'rules.ndjson')
|
||||
.expect('Content-Type', 'application/json; charset=utf-8')
|
||||
.expect(200);
|
||||
};
|
||||
|
||||
const prebuiltRules = SAMPLE_PREBUILT_RULES_WITH_HISTORICAL_VERSIONS.map(
|
||||
(prebuiltRule) => prebuiltRule['security-rule']
|
||||
);
|
||||
const prebuiltRuleIds = [...new Set(prebuiltRules.map((rule) => rule.rule_id))];
|
||||
|
||||
describe('@ess @serverless @skipInServerlessMKI import_rules', () => {
|
||||
before(async () => {
|
||||
await deleteAllPrebuiltRuleAssets(es, log);
|
||||
await createHistoricalPrebuiltRuleAssetSavedObjects(
|
||||
es,
|
||||
SAMPLE_PREBUILT_RULES_WITH_HISTORICAL_VERSIONS
|
||||
);
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await deleteAllPrebuiltRuleAssets(es, log);
|
||||
await deleteAllRules(supertest, log);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await deleteAllRules(supertest, log);
|
||||
});
|
||||
|
||||
describe('calculation of rule customization fields', () => {
|
||||
it('defaults a versionless custom rule to "version: 1"', async () => {
|
||||
const rule = getCustomQueryRuleParams({ rule_id: 'custom-rule', version: undefined });
|
||||
const { body } = await importRules([rule]);
|
||||
|
||||
expect(body).toMatchObject({
|
||||
rules_count: 1,
|
||||
success: true,
|
||||
success_count: 1,
|
||||
errors: [],
|
||||
});
|
||||
|
||||
const importedRule = await fetchRule(supertest, { ruleId: rule.rule_id! });
|
||||
expect(importedRule).toMatchObject({
|
||||
rule_id: rule.rule_id,
|
||||
version: 1,
|
||||
rule_source: { type: 'internal' },
|
||||
immutable: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('preserves a custom rule with a specified version', async () => {
|
||||
const rule = getCustomQueryRuleParams({ rule_id: 'custom-rule', version: 23 });
|
||||
const { body } = await importRules([rule]);
|
||||
|
||||
expect(body).toMatchObject({
|
||||
rules_count: 1,
|
||||
success: true,
|
||||
success_count: 1,
|
||||
errors: [],
|
||||
});
|
||||
|
||||
const importedRule = await fetchRule(supertest, { ruleId: rule.rule_id! });
|
||||
expect(importedRule).toMatchObject({
|
||||
rule_id: rule.rule_id,
|
||||
version: 23,
|
||||
rule_source: { type: 'internal' },
|
||||
immutable: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects a versionless prebuilt rule', async () => {
|
||||
const rule = getCustomQueryRuleParams({ rule_id: prebuiltRuleIds[0], version: undefined });
|
||||
const { body } = await importRules([rule]);
|
||||
|
||||
expect(body.errors).toHaveLength(1);
|
||||
expect(body.errors[0]).toMatchObject({
|
||||
error: {
|
||||
message: `Prebuilt rules must specify a "version" to be imported. [rule_id: ${prebuiltRuleIds[0]}]`,
|
||||
status_code: 400,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('respects the version of a prebuilt rule', async () => {
|
||||
const rule = getCustomQueryRuleParams({ rule_id: prebuiltRuleIds[1], version: 9999 });
|
||||
const { body } = await importRules([rule]);
|
||||
|
||||
expect(body).toMatchObject({
|
||||
rules_count: 1,
|
||||
success: true,
|
||||
success_count: 1,
|
||||
errors: [],
|
||||
});
|
||||
|
||||
const importedRule = await fetchRule(supertest, { ruleId: rule.rule_id! });
|
||||
expect(importedRule).toMatchObject({
|
||||
rule_id: rule.rule_id,
|
||||
version: 9999,
|
||||
rule_source: { type: 'external', is_customized: true },
|
||||
immutable: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('imports a combination of prebuilt and custom rules', async () => {
|
||||
const rules = [
|
||||
getCustomQueryRuleParams({ rule_id: 'custom-rule', version: 23 }),
|
||||
getCustomQueryRuleParams({ rule_id: prebuiltRuleIds[0], version: 1234 }),
|
||||
getCustomQueryRuleParams({ rule_id: 'custom-rule-2', version: undefined }),
|
||||
prebuiltRules[3],
|
||||
];
|
||||
const { body } = await importRules(rules);
|
||||
|
||||
expect(body).toMatchObject({
|
||||
rules_count: 4,
|
||||
success: true,
|
||||
success_count: 4,
|
||||
errors: [],
|
||||
});
|
||||
|
||||
const { data: importedRules } = await getInstalledRules(supertest);
|
||||
|
||||
expect(importedRules).toHaveLength(4);
|
||||
expect(importedRules).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
rule_id: 'custom-rule',
|
||||
version: 23,
|
||||
rule_source: { type: 'internal' },
|
||||
immutable: false,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
rule_id: prebuiltRuleIds[0],
|
||||
version: 1234,
|
||||
rule_source: { type: 'external', is_customized: true },
|
||||
immutable: true,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
rule_id: 'custom-rule-2',
|
||||
version: 1,
|
||||
rule_source: { type: 'internal' },
|
||||
immutable: false,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
rule_id: prebuiltRules[3].rule_id,
|
||||
version: prebuiltRules[3].version,
|
||||
rule_source: { type: 'external', is_customized: false },
|
||||
immutable: true,
|
||||
}),
|
||||
])
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
|
@ -8,7 +8,7 @@
|
|||
import { FtrProviderContext } from '../../../../../../ftr_provider_context';
|
||||
|
||||
export default ({ loadTestFile }: FtrProviderContext): void => {
|
||||
describe('Rules Management - Prebuilt Rules - Update Prebuilt Rules Package', function () {
|
||||
describe('Rules Management - Prebuilt Rules - Prebuilt Rule Customization Enabled', function () {
|
||||
loadTestFile(require.resolve('./is_customized_calculation'));
|
||||
loadTestFile(require.resolve('./rules_export'));
|
||||
});
|
|
@ -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 () => {
|
||||
|
@ -261,10 +253,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 }),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue