[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:
Dmitrii Shevchenko 2024-12-04 13:06:34 +01:00 committed by GitHub
parent e21bfd6186
commit e2eaf0c7d2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 586 additions and 34 deletions

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -51,6 +51,7 @@ describe('DetectionRulesClient.createCustomRule', () => {
rulesClient,
mlAuthz,
savedObjectsClient,
isRuleCustomizationEnabled: true,
});
});

View file

@ -44,6 +44,7 @@ describe('DetectionRulesClient.createPrebuiltRule', () => {
rulesClient,
mlAuthz,
savedObjectsClient,
isRuleCustomizationEnabled: true,
});
});

View file

@ -30,6 +30,7 @@ describe('DetectionRulesClient.deleteRule', () => {
rulesClient,
mlAuthz,
savedObjectsClient,
isRuleCustomizationEnabled: true,
});
});

View file

@ -55,6 +55,7 @@ describe('DetectionRulesClient.importRule', () => {
rulesClient,
mlAuthz,
savedObjectsClient,
isRuleCustomizationEnabled: true,
});
});

View file

@ -50,6 +50,7 @@ describe('DetectionRulesClient.patchRule', () => {
rulesClient,
mlAuthz,
savedObjectsClient,
isRuleCustomizationEnabled: true,
});
});

View file

@ -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,
});
});
},

View file

@ -50,6 +50,7 @@ describe('DetectionRulesClient.updateRule', () => {
rulesClient,
mlAuthz,
savedObjectsClient,
isRuleCustomizationEnabled: true,
});
});

View file

@ -48,6 +48,7 @@ describe('DetectionRulesClient.upgradePrebuiltRule', () => {
rulesClient,
mlAuthz,
savedObjectsClient,
isRuleCustomizationEnabled: true,
});
});

View file

@ -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({

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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({

View file

@ -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',

View file

@ -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({

View file

@ -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({

View file

@ -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({

View file

@ -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({

View file

@ -139,6 +139,7 @@ export class RequestContextFactory implements IRequestContextFactory {
rulesClient: startPlugins.alerting.getRulesClientWithRequest(request),
savedObjectsClient: coreContext.savedObjects.client,
mlAuthz,
isRuleCustomizationEnabled: config.experimentalFeatures.prebuiltRulesCustomizationEnabled,
});
}),

View file

@ -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;
}

View file

@ -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: [],
});

View file

@ -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'));
});
};

View file

@ -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);
});
});
};

View file

@ -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) => {

View file

@ -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([

View file

@ -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,
}),
])
);
});
});
});
};

View file

@ -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'));
});

View file

@ -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 }),