Skip to content

Commit

Permalink
[Security Solution] Skip isCustomized calculation when the feature fl…
Browse files Browse the repository at this point in the history
…ag is off (elastic#201825)

**Resolves: elastic#201632

## Summary  

When the rule customization feature flag is disabled, we should always
return `isCustomized: false`, regardless of any changes introduced to a
rule. This ensures that we do not accidentally mark prebuilt rules as
customized in 8.16 with the feature flag off. For more details, refer to
the related issue: elastic#201632

### Main Changes  

- The primary change in this PR is encapsulated in the
`calculateIsCustomized` function
- Other changes involve passing the feature flag to this function
- Added integration tests to cover all API CRUD operations that can be
performed with rules
xcrzx authored and CAWilson94 committed Dec 12, 2024
1 parent c978573 commit 652f311
Showing 40 changed files with 425 additions and 36 deletions.
3 changes: 2 additions & 1 deletion .buildkite/ftr_security_serverless_configs.yml
Original file line number Diff line number Diff line change
@@ -76,7 +76,8 @@ enabled:
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/large_prebuilt_rules_package/trial_license_complete_tier/configs/serverless.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/configs/serverless.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/update_prebuilt_rules_package/trial_license_complete_tier/configs/serverless.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/trial_license_complete_tier/configs/serverless.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/configs/serverless.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/configs/serverless.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_bulk_actions/trial_license_complete_tier/configs/serverless.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_delete/trial_license_complete_tier/configs/serverless.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_delete/basic_license_essentials_tier/configs/serverless.config.ts
3 changes: 2 additions & 1 deletion .buildkite/ftr_security_stateful_configs.yml
Original file line number Diff line number Diff line change
@@ -58,7 +58,8 @@ enabled:
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/large_prebuilt_rules_package/trial_license_complete_tier/configs/ess.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/management/trial_license_complete_tier/configs/ess.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/update_prebuilt_rules_package/trial_license_complete_tier/configs/ess.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/trial_license_complete_tier/configs/ess.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_enabled/configs/ess.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/prebuilt_rules/prebuilt_rule_customization/customization_disabled/configs/ess.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_bulk_actions/trial_license_complete_tier/configs/ess.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_delete/trial_license_complete_tier/configs/ess.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/rules_management/rule_delete/basic_license_essentials_tier/configs/ess.config.ts
Original file line number Diff line number Diff line change
@@ -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,
Original file line number Diff line number Diff line change
@@ -51,6 +51,7 @@ describe('DetectionRulesClient.createCustomRule', () => {
rulesClient,
mlAuthz,
savedObjectsClient,
isRuleCustomizationEnabled: true,
});
});

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

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

Original file line number Diff line number Diff line change
@@ -51,6 +51,7 @@ describe('DetectionRulesClient.importRule', () => {
rulesClient,
mlAuthz,
savedObjectsClient,
isRuleCustomizationEnabled: true,
});
});

Original file line number Diff line number Diff line change
@@ -32,6 +32,7 @@ describe('detectionRulesClient.importRules', () => {
rulesClient: rulesClientMock.create(),
mlAuthz: buildMlAuthz(),
savedObjectsClient: savedObjectsClientMock.create(),
isRuleCustomizationEnabled: true,
});

(checkRuleExceptionReferences as jest.Mock).mockReturnValue([[], []]);
Original file line number Diff line number Diff line change
@@ -50,6 +50,7 @@ describe('DetectionRulesClient.patchRule', () => {
rulesClient,
mlAuthz,
savedObjectsClient,
isRuleCustomizationEnabled: true,
});
});

Original file line number Diff line number Diff line change
@@ -38,13 +38,15 @@ interface DetectionRulesClientParams {
rulesClient: RulesClient;
savedObjectsClient: SavedObjectsClientContract;
mlAuthz: MlAuthz;
isRuleCustomizationEnabled: boolean;
}

export const createDetectionRulesClient = ({
actionsClient,
rulesClient,
mlAuthz,
savedObjectsClient,
isRuleCustomizationEnabled,
}: DetectionRulesClientParams): IDetectionRulesClient => {
const prebuiltRuleAssetClient = createPrebuiltRuleAssetsClient(savedObjectsClient);

@@ -89,6 +91,7 @@ export const createDetectionRulesClient = ({
prebuiltRuleAssetClient,
mlAuthz,
ruleUpdate,
isRuleCustomizationEnabled,
});
});
},
@@ -101,6 +104,7 @@ export const createDetectionRulesClient = ({
prebuiltRuleAssetClient,
mlAuthz,
rulePatch,
isRuleCustomizationEnabled,
});
});
},
@@ -119,6 +123,7 @@ export const createDetectionRulesClient = ({
ruleAsset,
mlAuthz,
prebuiltRuleAssetClient,
isRuleCustomizationEnabled,
});
});
},
@@ -131,6 +136,7 @@ export const createDetectionRulesClient = ({
importRulePayload: args,
mlAuthz,
prebuiltRuleAssetClient,
isRuleCustomizationEnabled,
});
});
},
Original file line number Diff line number Diff line change
@@ -50,6 +50,7 @@ describe('DetectionRulesClient.updateRule', () => {
rulesClient,
mlAuthz,
savedObjectsClient,
isRuleCustomizationEnabled: true,
});
});

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

Original file line number Diff line number Diff line change
@@ -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({
Original file line number Diff line number Diff line change
@@ -51,13 +51,15 @@ interface ApplyRulePatchProps {
prebuiltRuleAssetClient: IPrebuiltRuleAssetsClient;
existingRule: RuleResponse;
rulePatch: PatchRuleRequestBody;
isRuleCustomizationEnabled: boolean;
}

// eslint-disable-next-line complexity
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;
Original file line number Diff line number Diff line change
@@ -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;
Loading

0 comments on commit 652f311

Please sign in to comment.