From 6150a221c26369a67031fbf5d9147b231372f31a Mon Sep 17 00:00:00 2001 From: Dmitrii Shevchenko Date: Thu, 23 May 2024 10:25:46 +0200 Subject: [PATCH] [Security Solution] Replaced the incorrect runtime type used for ruleSource (#184004) ## Summary This PR replaces the incorrect Zod schema for the `ruleSource` rule param. Previously, the rule source field schema was implemented using a Zod transformation that automatically converted the snake-cased `is_customized` attribute to its camel-cased version `isCustomized`. ```ts const RuleSourceCamelCased = RuleSource.transform(convertObjectKeysToCamelCase); const RuleSource = z.object({ type: z.literal('external'), is_customized: IsExternalRuleCustomized, }); ``` However, this meant that the expected input type for the schema was snake-cased, as the transformation happened only after validation. **Valid payload before:** ```json5 { "ruleSource": { "type": "external", "is_customized": false // <- it should be camel cased } } ``` To overcome this issue, the rule source schema was implemented without using the transformation (revert https://github.com/elastic/kibana/issues/180121). **Valid payload after:** ```json5 { "ruleSource": { "type": "external", "isCustomized": false } } ``` ### Important Note This rule param schema change is considered safe because we do not currently use this field in the code. All values of this field are currently `undefined`. However, to ensure a Serverless release rollout without breaking changes, we need to release this schema change before we start writing any actual data. --- ...s_upgrade_and_rollback_checks.test.ts.snap | 28 +++++++++---------- .../rule_schema/model/rule_schemas.ts | 17 +++++++++-- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/x-pack/plugins/alerting/server/integration_tests/__snapshots__/serverless_upgrade_and_rollback_checks.test.ts.snap b/x-pack/plugins/alerting/server/integration_tests/__snapshots__/serverless_upgrade_and_rollback_checks.test.ts.snap index 518c2b406db74..c0827083779f3 100644 --- a/x-pack/plugins/alerting/server/integration_tests/__snapshots__/serverless_upgrade_and_rollback_checks.test.ts.snap +++ b/x-pack/plugins/alerting/server/integration_tests/__snapshots__/serverless_upgrade_and_rollback_checks.test.ts.snap @@ -6097,7 +6097,7 @@ Object { Object { "additionalProperties": false, "properties": Object { - "is_customized": Object { + "isCustomized": Object { "type": "boolean", }, "type": Object { @@ -6107,7 +6107,7 @@ Object { }, "required": Array [ "type", - "is_customized", + "isCustomized", ], "type": "object", }, @@ -6596,7 +6596,7 @@ Object { Object { "additionalProperties": false, "properties": Object { - "is_customized": Object { + "isCustomized": Object { "type": "boolean", }, "type": Object { @@ -6606,7 +6606,7 @@ Object { }, "required": Array [ "type", - "is_customized", + "isCustomized", ], "type": "object", }, @@ -7158,7 +7158,7 @@ Object { Object { "additionalProperties": false, "properties": Object { - "is_customized": Object { + "isCustomized": Object { "type": "boolean", }, "type": Object { @@ -7168,7 +7168,7 @@ Object { }, "required": Array [ "type", - "is_customized", + "isCustomized", ], "type": "object", }, @@ -7592,7 +7592,7 @@ Object { Object { "additionalProperties": false, "properties": Object { - "is_customized": Object { + "isCustomized": Object { "type": "boolean", }, "type": Object { @@ -7602,7 +7602,7 @@ Object { }, "required": Array [ "type", - "is_customized", + "isCustomized", ], "type": "object", }, @@ -8132,7 +8132,7 @@ Object { Object { "additionalProperties": false, "properties": Object { - "is_customized": Object { + "isCustomized": Object { "type": "boolean", }, "type": Object { @@ -8142,7 +8142,7 @@ Object { }, "required": Array [ "type", - "is_customized", + "isCustomized", ], "type": "object", }, @@ -8844,7 +8844,7 @@ Object { Object { "additionalProperties": false, "properties": Object { - "is_customized": Object { + "isCustomized": Object { "type": "boolean", }, "type": Object { @@ -8854,7 +8854,7 @@ Object { }, "required": Array [ "type", - "is_customized", + "isCustomized", ], "type": "object", }, @@ -9556,7 +9556,7 @@ Object { Object { "additionalProperties": false, "properties": Object { - "is_customized": Object { + "isCustomized": Object { "type": "boolean", }, "type": Object { @@ -9566,7 +9566,7 @@ Object { }, "required": Array [ "type", - "is_customized", + "isCustomized", ], "type": "object", }, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts index 120ddd3981165..48637e898dda3 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_schema/model/rule_schemas.ts @@ -41,6 +41,7 @@ import { IndexPatternArray, InvestigationFields, InvestigationGuide, + IsExternalRuleCustomized, IsRuleImmutable, ItemsPerSearch, KqlQueryLanguage, @@ -63,7 +64,6 @@ import { RuleQuery, RuleReferenceArray, RuleSignatureId, - RuleSource, RuleVersion, SavedQueryId, SetupGuide, @@ -84,7 +84,6 @@ import { TimestampOverrideFallbackDisabled, } from '../../../../../common/api/detection_engine/model/rule_schema'; import type { SERVER_APP_ID } from '../../../../../common/constants'; -import { convertObjectKeysToCamelCase } from '../../../../utils/object_case_converters'; // 8.10.x is mapped as an array of strings export type LegacyInvestigationFields = z.infer; @@ -104,8 +103,20 @@ export const InvestigationFieldsCombined = z.union([ LegacyInvestigationFields, ]); +/** + * This is the same type as RuleSource, but with the keys in camelCase. Intended + * for internal use only (not for API responses). + */ export type RuleSourceCamelCased = z.infer; -export const RuleSourceCamelCased = RuleSource.transform(convertObjectKeysToCamelCase); +export const RuleSourceCamelCased = z.discriminatedUnion('type', [ + z.object({ + type: z.literal('external'), + isCustomized: IsExternalRuleCustomized, + }), + z.object({ + type: z.literal('internal'), + }), +]); // Conversion to an interface has to be disabled for the entire file; otherwise, // the resulting union would not be assignable to Alerting's RuleParams due to a