From 6cc415c332824c9c3ba162f79138f8e91a96f9b6 Mon Sep 17 00:00:00 2001 From: Marshall Main <55718608+marshallmain@users.noreply.github.com> Date: Wed, 29 Nov 2023 12:11:29 -0800 Subject: [PATCH] [Security Solution] Swap rule unions out for discriminated unions to improve validation error messages (#171452) **Epics:** https://github.com/elastic/security-team/issues/8058, https://github.com/elastic/security-team/issues/6726 (internal) **Partially addresses:** https://github.com/elastic/security-team/issues/7991 (internal) ## Summary The main benefit of this PR is shown in `rule_request_schema.test.ts`, where the error messages are now more accurate and concise. With regular unions, `zod` has to try validating the input against all schemas in the union and reports the errors from every schema in the union. Switching to discriminated unions, with `type` as the discriminator, allows `zod` to pick the right rule type schema from the union and only validate against that rule type. This means the error message reports that either the discriminator is invalid, in any case where `type` is not valid, or if `type` is valid but another field is wrong for that type of rule then the error message is the validation result from only that rule type. To make it possible to use discriminated unions, we need to switch from using zod's `.and()` for intersections to `.merge()` because `.and()` returns an intersection type that is incompatible with discriminated unions in zod. Similarly, we need to remove the usage of `.transform()` because it returns a ZodEffect that is incompatible with `.merge()`. Instead of using `.transform()` to turn properties from optional to possibly undefined, we can use `requiredOptional` explicitly in specific places to convert the types. Similarly, the `RequiredOptional` type can be used on the return type of conversion functions between API and internal schemas to enforce that all properties are explicitly specified in the conversion. Future work: - better alignment of codegen with OpenAPI definitions of anyOf/oneOf. https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/#oneof oneOf requires that the input match exactly one schema from the list, which is different from z.union. anyOf should be z.union, oneOf should be z.discriminatedUnion - flatten the schema structure further to avoid `Type instantiation is excessively deep and possibly infinite`. Seems to be a common issue with zod (https://github.com/microsoft/TypeScript/issues/34933) Limiting the number of `.merge` and other zod operations needed to build a particular schema object seems to help resolve the error. Combining `ResponseRequiredFields` and `ResponseOptionalFields` into a single object rather than merging them solved the immediate problem. However, we may still be near the depth limit. Changing `RuleResponse` as seen below also solved the problem in testing, and may give us more headroom for future changes if we apply techniques like this here and in other places. The difference here is that `SharedResponseProps` is only intersected with the type specific schemas after they're combined in a discriminated union, whereas in `main` we merge `SharedResponseProps` with each individual schema then merge them all together. - combine other Required and Optional schemas, like QueryRuleRequiredFields and QueryRuleOptionalFields ```ts export type RuleResponse = z.infer; export const RuleResponse = SharedResponseProps.and(z.discriminatedUnion('type', [ EqlRuleResponseFields, QueryRuleResponseFields, SavedQueryRuleResponseFields, ThresholdRuleResponseFields, ThreatMatchRuleResponseFields, MachineLearningRuleResponseFields, NewTermsRuleResponseFields, EsqlRuleResponseFields, ])); ``` --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../templates/zod_schema_item.handlebars | 24 ++- .../response_actions.gen.ts | 81 ++++----- .../response_actions.schema.yaml | 4 - .../rule_schema/common_attributes.gen.ts | 16 +- .../rule_schema/common_attributes.schema.yaml | 1 - .../rule_schema/rule_request_schema.test.ts | 140 ++++++--------- .../rule_schema/rule_response_schema.test.ts | 10 +- .../model/rule_schema/rule_schemas.gen.ts | 161 +++++++++--------- .../rule_schema/rule_schemas.schema.yaml | 23 ++- .../bulk_actions/bulk_actions_route.gen.ts | 12 +- .../bulk_create_rules_route.test.ts | 20 +-- .../bulk_update_rules_route.test.ts | 20 +-- .../bulk_crud/response_schema.test.ts | 4 +- .../import_rules/rule_to_import.test.ts | 14 +- .../import_rules/rule_to_import.ts | 4 +- .../api/endpoint/actions/execute.gen.ts | 2 +- .../api/endpoint/actions/file_upload.gen.ts | 2 +- .../api/endpoint/actions/get_file.gen.ts | 2 +- .../api/endpoint/model/schema/common.gen.ts | 2 +- .../rule_details/rule_about_section.tsx | 5 +- .../pages/detection_engine/rules/helpers.tsx | 3 +- .../normalization/convert_rule_to_diffable.ts | 3 +- .../rule_assets/prebuilt_rule_asset.test.ts | 10 +- .../api/rules/create_rule/route.test.ts | 2 +- .../api/rules/update_rule/route.test.ts | 2 +- .../create_rules_stream_from_ndjson.test.ts | 2 +- .../rule_management/utils/validate.test.ts | 5 +- .../osquery_response_action.ts | 5 +- .../rule_types/factories/utils/build_alert.ts | 3 +- .../factories/utils/build_bulk_body.ts | 3 +- .../group1/create_rules_bulk.ts | 2 +- .../group10/import_rules.ts | 6 +- .../group10/update_rules.ts | 5 +- .../group10/update_rules_bulk.ts | 2 +- .../rule_creation/create_rules.ts | 5 +- 35 files changed, 272 insertions(+), 333 deletions(-) diff --git a/packages/kbn-openapi-generator/src/template_service/templates/zod_schema_item.handlebars b/packages/kbn-openapi-generator/src/template_service/templates/zod_schema_item.handlebars index 42b1f94f5925dae..29d4453da4d7be9 100644 --- a/packages/kbn-openapi-generator/src/template_service/templates/zod_schema_item.handlebars +++ b/packages/kbn-openapi-generator/src/template_service/templates/zod_schema_item.handlebars @@ -12,7 +12,6 @@ {{~#if (defined default)}}.default({{{toJSON default}}}){{/if~}} {{~#if (eq x-modify "partial")}}.partial(){{/if~}} {{~#if (eq x-modify "required")}}.required(){{/if~}} - {{~#if (eq x-modify "requiredOptional")}}.transform(requiredOptional){{/if~}} {{~/if~}} {{~#if allOf~}} @@ -20,26 +19,34 @@ {{~#if @first~}} {{> zod_schema_item }} {{~else~}} - .and({{> zod_schema_item }}) + .merge({{> zod_schema_item }}) {{~/if~}} {{~/each~}} {{~/if~}} {{~#if anyOf~}} - z.union([ - {{~#each anyOf~}} + {{#if discriminator}} + z.discriminatedUnion('{{discriminator.propertyName}}', [ + {{else}} + z.union([ + {{/if}} + {{~#each anyOf~}} {{~> zod_schema_item ~}}, - {{~/each~}} + {{~/each~}} ]) {{~#if nullable}}.nullable(){{/if~}} {{~#if (eq requiredBool false)}}.optional(){{/if~}} {{~/if~}} {{~#if oneOf~}} - z.union([ - {{~#each oneOf~}} + {{#if discriminator}} + z.discriminatedUnion('{{discriminator.propertyName}}', [ + {{else}} + z.union([ + {{/if}} + {{~#each oneOf~}} {{~> zod_schema_item ~}}, - {{~/each~}} + {{~/each~}} ]) {{~#if nullable}}.nullable(){{/if~}} {{~#if (eq requiredBool false)}}.optional(){{/if~}} @@ -97,7 +104,6 @@ z.unknown() {{~/if~}} {{~#if (eq x-modify "partial")}}.partial(){{/if~}} {{~#if (eq x-modify "required")}}.required(){{/if~}} - {{~#if (eq x-modify "requiredOptional")}}.transform(requiredOptional){{/if~}} {{~/inline~}} {{~#*inline "type_string"~}} diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_response_actions/response_actions.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_response_actions/response_actions.gen.ts index 10901049d476d35..eebcde55a030129 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_response_actions/response_actions.gen.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_response_actions/response_actions.gen.ts @@ -6,7 +6,6 @@ */ import { z } from 'zod'; -import { requiredOptional } from '@kbn/zod-helpers'; /* * NOTICE: Do not edit this file manually. @@ -27,48 +26,42 @@ export const EcsMapping = z.object({}).catchall( ); export type OsqueryQuery = z.infer; -export const OsqueryQuery = z - .object({ - /** - * Query ID - */ - id: z.string(), - /** - * Query to execute - */ - query: z.string(), - ecs_mapping: EcsMapping.optional(), - /** - * Query version - */ - version: z.string().optional(), - platform: z.string().optional(), - removed: z.boolean().optional(), - snapshot: z.boolean().optional(), - }) - .transform(requiredOptional); +export const OsqueryQuery = z.object({ + /** + * Query ID + */ + id: z.string(), + /** + * Query to execute + */ + query: z.string(), + ecs_mapping: EcsMapping.optional(), + /** + * Query version + */ + version: z.string().optional(), + platform: z.string().optional(), + removed: z.boolean().optional(), + snapshot: z.boolean().optional(), +}); export type OsqueryParams = z.infer; -export const OsqueryParams = z - .object({ - query: z.string().optional(), - ecs_mapping: EcsMapping.optional(), - queries: z.array(OsqueryQuery).optional(), - pack_id: z.string().optional(), - saved_query_id: z.string().optional(), - }) - .transform(requiredOptional); +export const OsqueryParams = z.object({ + query: z.string().optional(), + ecs_mapping: EcsMapping.optional(), + queries: z.array(OsqueryQuery).optional(), + pack_id: z.string().optional(), + saved_query_id: z.string().optional(), +}); export type OsqueryParamsCamelCase = z.infer; -export const OsqueryParamsCamelCase = z - .object({ - query: z.string().optional(), - ecsMapping: EcsMapping.optional(), - queries: z.array(OsqueryQuery).optional(), - packId: z.string().optional(), - savedQueryId: z.string().optional(), - }) - .transform(requiredOptional); +export const OsqueryParamsCamelCase = z.object({ + query: z.string().optional(), + ecsMapping: EcsMapping.optional(), + queries: z.array(OsqueryQuery).optional(), + packId: z.string().optional(), + savedQueryId: z.string().optional(), +}); export type OsqueryResponseAction = z.infer; export const OsqueryResponseAction = z.object({ @@ -83,12 +76,10 @@ export const RuleResponseOsqueryAction = z.object({ }); export type EndpointParams = z.infer; -export const EndpointParams = z - .object({ - command: z.literal('isolate'), - comment: z.string().optional(), - }) - .transform(requiredOptional); +export const EndpointParams = z.object({ + command: z.literal('isolate'), + comment: z.string().optional(), +}); export type EndpointResponseAction = z.infer; export const EndpointResponseAction = z.object({ diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_response_actions/response_actions.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_response_actions/response_actions.schema.yaml index 6cc6f0c46465cd7..9d27bb6e4a97ce7 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_response_actions/response_actions.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_response_actions/response_actions.schema.yaml @@ -49,7 +49,6 @@ components: required: - id - query - x-modify: requiredOptional OsqueryParams: type: object @@ -66,7 +65,6 @@ components: type: string saved_query_id: type: string - x-modify: requiredOptional OsqueryParamsCamelCase: type: object @@ -83,7 +81,6 @@ components: type: string savedQueryId: type: string - x-modify: requiredOptional OsqueryResponseAction: type: object @@ -123,7 +120,6 @@ components: type: string required: - command - x-modify: requiredOptional EndpointResponseAction: type: object diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts index 79ad21ddfb00908..4b9001dc35c00f8 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.gen.ts @@ -6,7 +6,7 @@ */ import { z } from 'zod'; -import { requiredOptional, isValidDateMath } from '@kbn/zod-helpers'; +import { isValidDateMath } from '@kbn/zod-helpers'; /* * NOTICE: Do not edit this file manually. @@ -94,14 +94,12 @@ export const RiskScore = z.number().int().min(0).max(100); */ export type RiskScoreMapping = z.infer; export const RiskScoreMapping = z.array( - z - .object({ - field: z.string(), - operator: z.literal('equals'), - value: z.string(), - risk_score: RiskScore.optional(), - }) - .transform(requiredOptional) + z.object({ + field: z.string(), + operator: z.literal('equals'), + value: z.string(), + risk_score: RiskScore.optional(), + }) ); /** diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml index ad2bfaf76c4c018..047394c5843c78c 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/common_attributes.schema.yaml @@ -95,7 +95,6 @@ components: - field - operator - value - x-modify: requiredOptional description: Overrides generated alerts' risk_score with a value from the source event Severity: diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_request_schema.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_request_schema.test.ts index 062c91335440494..3dfc4a294965fcf 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_request_schema.test.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_request_schema.test.ts @@ -25,8 +25,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"name: Required, description: Required, risk_score: Required, severity: Required, type: Invalid literal value, expected \\"eql\\", and 52 more"` + expect(stringifyZodError(result.error)).toEqual( + "type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'" ); }); @@ -48,8 +48,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"name: Required, description: Required, risk_score: Required, severity: Required, type: Invalid literal value, expected \\"eql\\", and 52 more"` + expect(stringifyZodError(result.error)).toEqual( + "type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'" ); }); @@ -61,8 +61,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"name: Required, risk_score: Required, severity: Required, type: Invalid literal value, expected \\"eql\\", query: Required, and 44 more"` + expect(stringifyZodError(result.error)).toEqual( + "type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'" ); }); @@ -75,8 +75,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"name: Required, risk_score: Required, severity: Required, type: Invalid literal value, expected \\"eql\\", query: Required, and 44 more"` + expect(stringifyZodError(result.error)).toEqual( + "type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'" ); }); @@ -90,8 +90,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"name: Required, risk_score: Required, severity: Required, type: Invalid literal value, expected \\"eql\\", query: Required, and 44 more"` + expect(stringifyZodError(result.error)).toEqual( + "type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'" ); }); @@ -106,8 +106,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"risk_score: Required, severity: Required, type: Invalid literal value, expected \\"eql\\", query: Required, language: Invalid literal value, expected \\"eql\\", and 36 more"` + expect(stringifyZodError(result.error)).toEqual( + "type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'" ); }); @@ -123,8 +123,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"risk_score: Required, type: Invalid literal value, expected \\"eql\\", query: Required, language: Invalid literal value, expected \\"eql\\", risk_score: Required, and 28 more"` + expect(stringifyZodError(result.error)).toEqual( + "type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'" ); }); @@ -141,9 +141,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"risk_score: Required, type: Invalid literal value, expected \\"eql\\", query: Required, language: Invalid literal value, expected \\"eql\\", risk_score: Required, and 27 more"` - ); + expect(stringifyZodError(result.error)).toEqual('risk_score: Required'); }); test('[rule_id, description, from, to, name, severity, type, interval] does not validate', () => { @@ -160,9 +158,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"risk_score: Required, type: Invalid literal value, expected \\"eql\\", query: Required, language: Invalid literal value, expected \\"eql\\", risk_score: Required, and 27 more"` - ); + expect(stringifyZodError(result.error)).toEqual('risk_score: Required'); }); test('[rule_id, description, from, to, name, severity, type, interval, index] does not validate', () => { @@ -180,9 +176,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"risk_score: Required, type: Invalid literal value, expected \\"eql\\", query: Required, language: Invalid literal value, expected \\"eql\\", risk_score: Required, and 27 more"` - ); + expect(stringifyZodError(result.error)).toEqual('risk_score: Required'); }); test('[rule_id, description, from, to, name, severity, type, query, index, interval] does validate', () => { @@ -222,9 +216,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"risk_score: Required, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", risk_score: Required, risk_score: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('risk_score: Required'); }); test('[rule_id, description, from, to, index, name, severity, interval, type, query, language, risk_score] does validate', () => { @@ -390,8 +382,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"references.0: Expected string, received number, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", references.0: Expected string, received number, references.0: Expected string, received number, and 22 more"` + expect(stringifyZodError(result.error)).toEqual( + 'references.0: Expected string, received number' ); }); @@ -403,9 +395,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", index.0: Expected string, received number, index.0: Expected string, received number, type: Invalid literal value, expected \\"saved_query\\", and 20 more"` - ); + expect(stringifyZodError(result.error)).toEqual('index.0: Expected string, received number'); }); test('saved_query type can have filters with it', () => { @@ -427,9 +417,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", filters: Expected array, received string, filters: Expected array, received string, type: Invalid literal value, expected \\"saved_query\\", and 20 more"` - ); + expect(stringifyZodError(result.error)).toEqual('filters: Expected array, received string'); }); test('language validates with kuery', () => { @@ -462,8 +450,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", language: Invalid enum value. Expected 'kuery' | 'lucene', received 'something-made-up', type: Invalid literal value, expected \\"saved_query\\", saved_id: Required, and 19 more"` + expect(stringifyZodError(result.error)).toEqual( + "language: Invalid enum value. Expected 'kuery' | 'lucene', received 'something-made-up'" ); }); @@ -523,8 +511,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"tags.0: Expected string, received number, tags.1: Expected string, received number, tags.2: Expected string, received number, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", and 38 more"` + expect(stringifyZodError(result.error)).toEqual( + 'tags.0: Expected string, received number, tags.1: Expected string, received number, tags.2: Expected string, received number' ); }); @@ -551,9 +539,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"threat.0.framework: Required, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", threat.0.framework: Required, threat.0.framework: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('threat.0.framework: Required'); }); test('You cannot send in an array of threat that are missing "tactic"', () => { @@ -575,9 +561,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"threat.0.tactic: Required, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", threat.0.tactic: Required, threat.0.tactic: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('threat.0.tactic: Required'); }); test('You can send in an array of threat that are missing "technique"', () => { @@ -619,8 +603,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"false_positives.0: Expected string, received number, false_positives.1: Expected string, received number, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", false_positives.0: Expected string, received number, and 30 more"` + expect(stringifyZodError(result.error)).toEqual( + 'false_positives.0: Expected string, received number, false_positives.1: Expected string, received number' ); }); @@ -693,9 +677,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"meta: Expected object, received string, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", meta: Expected object, received string, meta: Expected object, received string, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('meta: Expected object, received string'); }); test('You can omit the query string when filters are present', () => { @@ -730,8 +712,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'junk', type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'junk', severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'junk', and 22 more"` + expect(stringifyZodError(result.error)).toEqual( + "severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'junk'" ); }); @@ -743,9 +725,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"actions.0.group: Required, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", actions.0.group: Required, actions.0.group: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('actions.0.group: Required'); }); test('You cannot send in an array of actions that are missing "id"', () => { @@ -756,9 +736,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"actions.0.id: Required, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", actions.0.id: Required, actions.0.id: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('actions.0.id: Required'); }); test('You cannot send in an array of actions that are missing "action_type_id"', () => { @@ -769,9 +747,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"actions.0.action_type_id: Required, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", actions.0.action_type_id: Required, actions.0.action_type_id: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('actions.0.action_type_id: Required'); }); test('You cannot send in an array of actions that are missing "params"', () => { @@ -782,9 +758,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"actions.0.params: Required, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", actions.0.params: Required, actions.0.params: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('actions.0.params: Required'); }); test('You cannot send in an array of actions that are including "actionTypeId"', () => { @@ -802,9 +776,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"actions.0.action_type_id: Required, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", actions.0.action_type_id: Required, actions.0.action_type_id: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('actions.0.action_type_id: Required'); }); describe('note', () => { @@ -840,9 +812,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"note: Expected string, received object, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", note: Expected string, received object, note: Expected string, received object, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('note: Expected string, received object'); }); test('empty name is not valid', () => { @@ -926,9 +896,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", type: Invalid literal value, expected \\"query\\", saved_id: Required, type: Invalid literal value, expected \\"threshold\\", and 14 more"` - ); + expect(stringifyZodError(result.error)).toEqual('saved_id: Required'); }); test('threshold is required when type is threshold and will not validate without it', () => { @@ -936,9 +904,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", type: Invalid literal value, expected \\"query\\", type: Invalid literal value, expected \\"saved_query\\", saved_id: Required, and 14 more"` - ); + expect(stringifyZodError(result.error)).toEqual('threshold: Required'); }); test('threshold rules fail validation if threshold is not greater than 0', () => { @@ -1016,8 +982,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"exceptions_list.0.list_id: Required, exceptions_list.0.type: Required, exceptions_list.0.namespace_type: Invalid enum value. Expected 'agnostic' | 'single', received 'not a namespace type', type: Invalid literal value, expected \\"eql\\", query: Required, and 43 more"` + expect(stringifyZodError(result.error)).toEqual( + "exceptions_list.0.list_id: Required, exceptions_list.0.type: Required, exceptions_list.0.namespace_type: Invalid enum value. Expected 'agnostic' | 'single', received 'not a namespace type'" ); }); @@ -1059,8 +1025,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", type: Invalid literal value, expected \\"query\\", type: Invalid literal value, expected \\"saved_query\\", saved_id: Required, and 14 more"` + expect(stringifyZodError(result.error)).toEqual( + 'threat_query: Required, threat_mapping: Required, threat_index: Required' ); }); @@ -1130,8 +1096,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", data_view_id: Expected string, received number, data_view_id: Expected string, received number, type: Invalid literal value, expected \\"saved_query\\", and 20 more"` + expect(stringifyZodError(result.error)).toEqual( + 'data_view_id: Expected string, received number' ); }); @@ -1195,9 +1161,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"investigation_fields.field_names: Required, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", investigation_fields.field_names: Required, investigation_fields.field_names: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('investigation_fields.field_names: Required'); }); test('You can send in investigation_fields', () => { @@ -1232,8 +1196,8 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"investigation_fields.field_names.0: Expected string, received number, investigation_fields.field_names.1: Expected string, received number, investigation_fields.field_names.2: Expected string, received number, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", and 38 more"` + expect(stringifyZodError(result.error)).toEqual( + 'investigation_fields.field_names.0: Expected string, received number, investigation_fields.field_names.1: Expected string, received number, investigation_fields.field_names.2: Expected string, received number' ); }); @@ -1245,9 +1209,7 @@ describe('rules schema', () => { const result = RuleCreateProps.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"investigation_fields.field_names: Required, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", investigation_fields.field_names: Required, investigation_fields.field_names: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toEqual('investigation_fields.field_names: Required'); }); }); }); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_response_schema.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_response_schema.test.ts index d1432e5a6735206..e05aa65c4fa0d00 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_response_schema.test.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_response_schema.test.ts @@ -41,7 +41,7 @@ describe('Rule response schema', () => { const result = RuleResponse.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", type: Invalid literal value, expected \\"query\\", type: Invalid literal value, expected \\"saved_query\\", saved_id: Required, and 15 more"` + `"type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'"` ); }); @@ -70,9 +70,7 @@ describe('Rule response schema', () => { const result = RuleResponse.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", type: Invalid literal value, expected \\"query\\", saved_id: Required, type: Invalid literal value, expected \\"threshold\\", and 14 more"` - ); + expect(stringifyZodError(result.error)).toMatchInlineSnapshot(`"saved_id: Required"`); }); test('it should validate a type of "timeline_id" if there is a "timeline_title" dependent', () => { @@ -103,7 +101,7 @@ describe('Rule response schema', () => { const result = RuleResponse.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"exceptions_list: Expected array, received string, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", exceptions_list: Expected array, received string, exceptions_list: Expected array, received string, and 22 more"` + `"exceptions_list: Expected array, received string"` ); }); }); @@ -239,7 +237,7 @@ describe('investigation_fields', () => { const result = RuleResponse.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"investigation_fields: Expected object, received string, type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", investigation_fields: Expected object, received string, investigation_fields: Expected object, received string, and 22 more"` + `"investigation_fields: Expected object, received string"` ); }); }); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.gen.ts index ff99655a75e8909..1051f59feac0ad3 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.gen.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.gen.ts @@ -131,20 +131,20 @@ export const BaseDefaultableFields = z.object({ export type BaseCreateProps = z.infer; export const BaseCreateProps = - BaseRequiredFields.and(BaseOptionalFields).and(BaseDefaultableFields); + BaseRequiredFields.merge(BaseOptionalFields).merge(BaseDefaultableFields); export type BasePatchProps = z.infer; export const BasePatchProps = BaseRequiredFields.partial() - .and(BaseOptionalFields) - .and(BaseDefaultableFields); + .merge(BaseOptionalFields) + .merge(BaseDefaultableFields); export type BaseResponseProps = z.infer; -export const BaseResponseProps = BaseRequiredFields.and(BaseOptionalFields).and( +export const BaseResponseProps = BaseRequiredFields.merge(BaseOptionalFields).merge( BaseDefaultableFields.required() ); -export type ResponseRequiredFields = z.infer; -export const ResponseRequiredFields = z.object({ +export type ResponseFields = z.infer; +export const ResponseFields = z.object({ id: RuleObjectId, rule_id: RuleSignatureId, immutable: IsRuleImmutable, @@ -156,22 +156,18 @@ export const ResponseRequiredFields = z.object({ related_integrations: RelatedIntegrationArray, required_fields: RequiredFieldArray, setup: SetupGuide, -}); - -export type ResponseOptionalFields = z.infer; -export const ResponseOptionalFields = z.object({ execution_summary: RuleExecutionSummary.optional(), }); export type SharedCreateProps = z.infer; -export const SharedCreateProps = BaseCreateProps.and( +export const SharedCreateProps = BaseCreateProps.merge( z.object({ rule_id: RuleSignatureId.optional(), }) ); export type SharedUpdateProps = z.infer; -export const SharedUpdateProps = BaseCreateProps.and( +export const SharedUpdateProps = BaseCreateProps.merge( z.object({ id: RuleObjectId.optional(), rule_id: RuleSignatureId.optional(), @@ -179,7 +175,7 @@ export const SharedUpdateProps = BaseCreateProps.and( ); export type SharedPatchProps = z.infer; -export const SharedPatchProps = BasePatchProps.and( +export const SharedPatchProps = BasePatchProps.merge( z.object({ id: RuleObjectId.optional(), rule_id: RuleSignatureId.optional(), @@ -187,8 +183,7 @@ export const SharedPatchProps = BasePatchProps.and( ); export type SharedResponseProps = z.infer; -export const SharedResponseProps = - BaseResponseProps.and(ResponseRequiredFields).and(ResponseOptionalFields); +export const SharedResponseProps = BaseResponseProps.merge(ResponseFields); export type EqlQueryLanguage = z.infer; export const EqlQueryLanguage = z.literal('eql'); @@ -220,25 +215,25 @@ export const EqlOptionalFields = z.object({ }); export type EqlRuleCreateFields = z.infer; -export const EqlRuleCreateFields = EqlRequiredFields.and(EqlOptionalFields); +export const EqlRuleCreateFields = EqlRequiredFields.merge(EqlOptionalFields); export type EqlRuleResponseFields = z.infer; -export const EqlRuleResponseFields = EqlRequiredFields.and(EqlOptionalFields); +export const EqlRuleResponseFields = EqlRequiredFields.merge(EqlOptionalFields); export type EqlRulePatchFields = z.infer; -export const EqlRulePatchFields = EqlRequiredFields.partial().and(EqlOptionalFields); +export const EqlRulePatchFields = EqlRequiredFields.partial().merge(EqlOptionalFields); export type EqlRule = z.infer; -export const EqlRule = SharedResponseProps.and(EqlRuleResponseFields); +export const EqlRule = SharedResponseProps.merge(EqlRuleResponseFields); export type EqlRuleCreateProps = z.infer; -export const EqlRuleCreateProps = SharedCreateProps.and(EqlRuleCreateFields); +export const EqlRuleCreateProps = SharedCreateProps.merge(EqlRuleCreateFields); export type EqlRuleUpdateProps = z.infer; -export const EqlRuleUpdateProps = SharedUpdateProps.and(EqlRuleCreateFields); +export const EqlRuleUpdateProps = SharedUpdateProps.merge(EqlRuleCreateFields); export type EqlRulePatchProps = z.infer; -export const EqlRulePatchProps = SharedPatchProps.and(EqlRulePatchFields); +export const EqlRulePatchProps = SharedPatchProps.merge(EqlRulePatchFields); export type QueryRuleRequiredFields = z.infer; export const QueryRuleRequiredFields = z.object({ @@ -265,31 +260,31 @@ export const QueryRuleDefaultableFields = z.object({ }); export type QueryRuleCreateFields = z.infer; -export const QueryRuleCreateFields = QueryRuleRequiredFields.and(QueryRuleOptionalFields).and( +export const QueryRuleCreateFields = QueryRuleRequiredFields.merge(QueryRuleOptionalFields).merge( QueryRuleDefaultableFields ); export type QueryRulePatchFields = z.infer; export const QueryRulePatchFields = QueryRuleRequiredFields.partial() - .and(QueryRuleOptionalFields) - .and(QueryRuleDefaultableFields); + .merge(QueryRuleOptionalFields) + .merge(QueryRuleDefaultableFields); export type QueryRuleResponseFields = z.infer; -export const QueryRuleResponseFields = QueryRuleRequiredFields.and(QueryRuleOptionalFields).and( +export const QueryRuleResponseFields = QueryRuleRequiredFields.merge(QueryRuleOptionalFields).merge( QueryRuleDefaultableFields.required() ); export type QueryRule = z.infer; -export const QueryRule = SharedResponseProps.and(QueryRuleResponseFields); +export const QueryRule = SharedResponseProps.merge(QueryRuleResponseFields); export type QueryRuleCreateProps = z.infer; -export const QueryRuleCreateProps = SharedCreateProps.and(QueryRuleCreateFields); +export const QueryRuleCreateProps = SharedCreateProps.merge(QueryRuleCreateFields); export type QueryRuleUpdateProps = z.infer; -export const QueryRuleUpdateProps = SharedUpdateProps.and(QueryRuleCreateFields); +export const QueryRuleUpdateProps = SharedUpdateProps.merge(QueryRuleCreateFields); export type QueryRulePatchProps = z.infer; -export const QueryRulePatchProps = SharedPatchProps.and(QueryRulePatchFields); +export const QueryRulePatchProps = SharedPatchProps.merge(QueryRulePatchFields); export type SavedQueryRuleRequiredFields = z.infer; export const SavedQueryRuleRequiredFields = z.object({ @@ -316,31 +311,31 @@ export const SavedQueryRuleDefaultableFields = z.object({ }); export type SavedQueryRuleCreateFields = z.infer; -export const SavedQueryRuleCreateFields = SavedQueryRuleRequiredFields.and( +export const SavedQueryRuleCreateFields = SavedQueryRuleRequiredFields.merge( SavedQueryRuleOptionalFields -).and(SavedQueryRuleDefaultableFields); +).merge(SavedQueryRuleDefaultableFields); export type SavedQueryRulePatchFields = z.infer; export const SavedQueryRulePatchFields = SavedQueryRuleRequiredFields.partial() - .and(SavedQueryRuleOptionalFields) - .and(SavedQueryRuleDefaultableFields); + .merge(SavedQueryRuleOptionalFields) + .merge(SavedQueryRuleDefaultableFields); export type SavedQueryRuleResponseFields = z.infer; -export const SavedQueryRuleResponseFields = SavedQueryRuleRequiredFields.and( +export const SavedQueryRuleResponseFields = SavedQueryRuleRequiredFields.merge( SavedQueryRuleOptionalFields -).and(SavedQueryRuleDefaultableFields.required()); +).merge(SavedQueryRuleDefaultableFields.required()); export type SavedQueryRule = z.infer; -export const SavedQueryRule = SharedResponseProps.and(SavedQueryRuleResponseFields); +export const SavedQueryRule = SharedResponseProps.merge(SavedQueryRuleResponseFields); export type SavedQueryRuleCreateProps = z.infer; -export const SavedQueryRuleCreateProps = SharedCreateProps.and(SavedQueryRuleCreateFields); +export const SavedQueryRuleCreateProps = SharedCreateProps.merge(SavedQueryRuleCreateFields); export type SavedQueryRuleUpdateProps = z.infer; -export const SavedQueryRuleUpdateProps = SharedUpdateProps.and(SavedQueryRuleCreateFields); +export const SavedQueryRuleUpdateProps = SharedUpdateProps.merge(SavedQueryRuleCreateFields); export type SavedQueryRulePatchProps = z.infer; -export const SavedQueryRulePatchProps = SharedPatchProps.and(SavedQueryRulePatchFields); +export const SavedQueryRulePatchProps = SharedPatchProps.merge(SavedQueryRulePatchFields); export type ThresholdRuleRequiredFields = z.infer; export const ThresholdRuleRequiredFields = z.object({ @@ -366,31 +361,31 @@ export const ThresholdRuleDefaultableFields = z.object({ }); export type ThresholdRuleCreateFields = z.infer; -export const ThresholdRuleCreateFields = ThresholdRuleRequiredFields.and( +export const ThresholdRuleCreateFields = ThresholdRuleRequiredFields.merge( ThresholdRuleOptionalFields -).and(ThresholdRuleDefaultableFields); +).merge(ThresholdRuleDefaultableFields); export type ThresholdRulePatchFields = z.infer; export const ThresholdRulePatchFields = ThresholdRuleRequiredFields.partial() - .and(ThresholdRuleOptionalFields) - .and(ThresholdRuleDefaultableFields); + .merge(ThresholdRuleOptionalFields) + .merge(ThresholdRuleDefaultableFields); export type ThresholdRuleResponseFields = z.infer; -export const ThresholdRuleResponseFields = ThresholdRuleRequiredFields.and( +export const ThresholdRuleResponseFields = ThresholdRuleRequiredFields.merge( ThresholdRuleOptionalFields -).and(ThresholdRuleDefaultableFields.required()); +).merge(ThresholdRuleDefaultableFields.required()); export type ThresholdRule = z.infer; -export const ThresholdRule = SharedResponseProps.and(ThresholdRuleResponseFields); +export const ThresholdRule = SharedResponseProps.merge(ThresholdRuleResponseFields); export type ThresholdRuleCreateProps = z.infer; -export const ThresholdRuleCreateProps = SharedCreateProps.and(ThresholdRuleCreateFields); +export const ThresholdRuleCreateProps = SharedCreateProps.merge(ThresholdRuleCreateFields); export type ThresholdRuleUpdateProps = z.infer; -export const ThresholdRuleUpdateProps = SharedUpdateProps.and(ThresholdRuleCreateFields); +export const ThresholdRuleUpdateProps = SharedUpdateProps.merge(ThresholdRuleCreateFields); export type ThresholdRulePatchProps = z.infer; -export const ThresholdRulePatchProps = SharedPatchProps.and(ThresholdRulePatchFields); +export const ThresholdRulePatchProps = SharedPatchProps.merge(ThresholdRulePatchFields); export type ThreatMatchRuleRequiredFields = z.infer; export const ThreatMatchRuleRequiredFields = z.object({ @@ -423,31 +418,31 @@ export const ThreatMatchRuleDefaultableFields = z.object({ }); export type ThreatMatchRuleCreateFields = z.infer; -export const ThreatMatchRuleCreateFields = ThreatMatchRuleRequiredFields.and( +export const ThreatMatchRuleCreateFields = ThreatMatchRuleRequiredFields.merge( ThreatMatchRuleOptionalFields -).and(ThreatMatchRuleDefaultableFields); +).merge(ThreatMatchRuleDefaultableFields); export type ThreatMatchRulePatchFields = z.infer; export const ThreatMatchRulePatchFields = ThreatMatchRuleRequiredFields.partial() - .and(ThreatMatchRuleOptionalFields) - .and(ThreatMatchRuleDefaultableFields); + .merge(ThreatMatchRuleOptionalFields) + .merge(ThreatMatchRuleDefaultableFields); export type ThreatMatchRuleResponseFields = z.infer; -export const ThreatMatchRuleResponseFields = ThreatMatchRuleRequiredFields.and( +export const ThreatMatchRuleResponseFields = ThreatMatchRuleRequiredFields.merge( ThreatMatchRuleOptionalFields -).and(ThreatMatchRuleDefaultableFields.required()); +).merge(ThreatMatchRuleDefaultableFields.required()); export type ThreatMatchRule = z.infer; -export const ThreatMatchRule = SharedResponseProps.and(ThreatMatchRuleResponseFields); +export const ThreatMatchRule = SharedResponseProps.merge(ThreatMatchRuleResponseFields); export type ThreatMatchRuleCreateProps = z.infer; -export const ThreatMatchRuleCreateProps = SharedCreateProps.and(ThreatMatchRuleCreateFields); +export const ThreatMatchRuleCreateProps = SharedCreateProps.merge(ThreatMatchRuleCreateFields); export type ThreatMatchRuleUpdateProps = z.infer; -export const ThreatMatchRuleUpdateProps = SharedUpdateProps.and(ThreatMatchRuleCreateFields); +export const ThreatMatchRuleUpdateProps = SharedUpdateProps.merge(ThreatMatchRuleCreateFields); export type ThreatMatchRulePatchProps = z.infer; -export const ThreatMatchRulePatchProps = SharedPatchProps.and(ThreatMatchRulePatchFields); +export const ThreatMatchRulePatchProps = SharedPatchProps.merge(ThreatMatchRulePatchFields); export type MachineLearningRuleRequiredFields = z.infer; export const MachineLearningRuleRequiredFields = z.object({ @@ -469,20 +464,20 @@ export type MachineLearningRuleCreateFields = z.infer; -export const MachineLearningRule = SharedResponseProps.and(MachineLearningRuleResponseFields); +export const MachineLearningRule = SharedResponseProps.merge(MachineLearningRuleResponseFields); export type MachineLearningRuleCreateProps = z.infer; -export const MachineLearningRuleCreateProps = SharedCreateProps.and( +export const MachineLearningRuleCreateProps = SharedCreateProps.merge( MachineLearningRuleCreateFields ); export type MachineLearningRuleUpdateProps = z.infer; -export const MachineLearningRuleUpdateProps = SharedUpdateProps.and( +export const MachineLearningRuleUpdateProps = SharedUpdateProps.merge( MachineLearningRuleCreateFields ); export type MachineLearningRulePatchProps = z.infer; -export const MachineLearningRulePatchProps = SharedPatchProps.and(MachineLearningRulePatchFields); +export const MachineLearningRulePatchProps = SharedPatchProps.merge(MachineLearningRulePatchFields); export type NewTermsRuleRequiredFields = z.infer; export const NewTermsRuleRequiredFields = z.object({ @@ -509,30 +504,30 @@ export const NewTermsRuleDefaultableFields = z.object({ export type NewTermsRulePatchFields = z.infer; export const NewTermsRulePatchFields = NewTermsRuleRequiredFields.partial() - .and(NewTermsRuleOptionalFields) - .and(NewTermsRuleDefaultableFields); + .merge(NewTermsRuleOptionalFields) + .merge(NewTermsRuleDefaultableFields); export type NewTermsRuleResponseFields = z.infer; -export const NewTermsRuleResponseFields = NewTermsRuleRequiredFields.and( +export const NewTermsRuleResponseFields = NewTermsRuleRequiredFields.merge( NewTermsRuleOptionalFields -).and(NewTermsRuleDefaultableFields.required()); +).merge(NewTermsRuleDefaultableFields.required()); export type NewTermsRuleCreateFields = z.infer; -export const NewTermsRuleCreateFields = NewTermsRuleRequiredFields.and( +export const NewTermsRuleCreateFields = NewTermsRuleRequiredFields.merge( NewTermsRuleOptionalFields -).and(NewTermsRuleDefaultableFields); +).merge(NewTermsRuleDefaultableFields); export type NewTermsRule = z.infer; -export const NewTermsRule = SharedResponseProps.and(NewTermsRuleResponseFields); +export const NewTermsRule = SharedResponseProps.merge(NewTermsRuleResponseFields); export type NewTermsRuleCreateProps = z.infer; -export const NewTermsRuleCreateProps = SharedCreateProps.and(NewTermsRuleCreateFields); +export const NewTermsRuleCreateProps = SharedCreateProps.merge(NewTermsRuleCreateFields); export type NewTermsRuleUpdateProps = z.infer; -export const NewTermsRuleUpdateProps = SharedUpdateProps.and(NewTermsRuleCreateFields); +export const NewTermsRuleUpdateProps = SharedUpdateProps.merge(NewTermsRuleCreateFields); export type NewTermsRulePatchProps = z.infer; -export const NewTermsRulePatchProps = SharedPatchProps.and(NewTermsRulePatchFields); +export const NewTermsRulePatchProps = SharedPatchProps.merge(NewTermsRulePatchFields); export type EsqlQueryLanguage = z.infer; export const EsqlQueryLanguage = z.literal('esql'); @@ -560,19 +555,19 @@ export type EsqlRuleCreateFields = z.infer; export const EsqlRuleCreateFields = EsqlRuleRequiredFields; export type EsqlRule = z.infer; -export const EsqlRule = SharedResponseProps.and(EsqlRuleResponseFields); +export const EsqlRule = SharedResponseProps.merge(EsqlRuleResponseFields); export type EsqlRuleCreateProps = z.infer; -export const EsqlRuleCreateProps = SharedCreateProps.and(EsqlRuleCreateFields); +export const EsqlRuleCreateProps = SharedCreateProps.merge(EsqlRuleCreateFields); export type EsqlRuleUpdateProps = z.infer; -export const EsqlRuleUpdateProps = SharedUpdateProps.and(EsqlRuleCreateFields); +export const EsqlRuleUpdateProps = SharedUpdateProps.merge(EsqlRuleCreateFields); export type EsqlRulePatchProps = z.infer; -export const EsqlRulePatchProps = SharedPatchProps.and(EsqlRulePatchFields.partial()); +export const EsqlRulePatchProps = SharedPatchProps.merge(EsqlRulePatchFields.partial()); export type TypeSpecificCreateProps = z.infer; -export const TypeSpecificCreateProps = z.union([ +export const TypeSpecificCreateProps = z.discriminatedUnion('type', [ EqlRuleCreateFields, QueryRuleCreateFields, SavedQueryRuleCreateFields, @@ -596,7 +591,7 @@ export const TypeSpecificPatchProps = z.union([ ]); export type TypeSpecificResponse = z.infer; -export const TypeSpecificResponse = z.union([ +export const TypeSpecificResponse = z.discriminatedUnion('type', [ EqlRuleResponseFields, QueryRuleResponseFields, SavedQueryRuleResponseFields, @@ -608,7 +603,7 @@ export const TypeSpecificResponse = z.union([ ]); export type RuleCreateProps = z.infer; -export const RuleCreateProps = z.union([ +export const RuleCreateProps = z.discriminatedUnion('type', [ EqlRuleCreateProps, QueryRuleCreateProps, SavedQueryRuleCreateProps, @@ -620,7 +615,7 @@ export const RuleCreateProps = z.union([ ]); export type RuleUpdateProps = z.infer; -export const RuleUpdateProps = z.union([ +export const RuleUpdateProps = z.discriminatedUnion('type', [ EqlRuleUpdateProps, QueryRuleUpdateProps, SavedQueryRuleUpdateProps, @@ -644,7 +639,7 @@ export const RulePatchProps = z.union([ ]); export type RuleResponse = z.infer; -export const RuleResponse = z.union([ +export const RuleResponse = z.discriminatedUnion('type', [ EqlRule, QueryRule, SavedQueryRule, diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml index 7ad80129ad78e73..a916e7e4da22496 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/model/rule_schema/rule_schemas.schema.yaml @@ -152,7 +152,7 @@ components: - $ref: '#/components/schemas/BaseDefaultableFields' x-modify: required - ResponseRequiredFields: + ResponseFields: type: object properties: id: @@ -185,6 +185,8 @@ components: $ref: './common_attributes.schema.yaml#/components/schemas/RequiredFieldArray' setup: $ref: './common_attributes.schema.yaml#/components/schemas/SetupGuide' + execution_summary: + $ref: '../../rule_monitoring/model/execution_summary.schema.yaml#/components/schemas/RuleExecutionSummary' required: - id - rule_id @@ -198,12 +200,6 @@ components: - required_fields - setup - ResponseOptionalFields: - type: object - properties: - execution_summary: - $ref: '../../rule_monitoring/model/execution_summary.schema.yaml#/components/schemas/RuleExecutionSummary' - SharedCreateProps: x-inline: true allOf: @@ -239,8 +235,7 @@ components: x-inline: true allOf: - $ref: '#/components/schemas/BaseResponseProps' - - $ref: '#/components/schemas/ResponseRequiredFields' - - $ref: '#/components/schemas/ResponseOptionalFields' + - $ref: '#/components/schemas/ResponseFields' ############ # EQL Rule # @@ -851,6 +846,8 @@ components: ########################## TypeSpecificCreateProps: + discriminator: + propertyName: type anyOf: - $ref: '#/components/schemas/EqlRuleCreateFields' - $ref: '#/components/schemas/QueryRuleCreateFields' @@ -873,6 +870,8 @@ components: - $ref: '#/components/schemas/EsqlRulePatchFields' TypeSpecificResponse: + discriminator: + propertyName: type anyOf: - $ref: '#/components/schemas/EqlRuleResponseFields' - $ref: '#/components/schemas/QueryRuleResponseFields' @@ -884,6 +883,8 @@ components: - $ref: '#/components/schemas/EsqlRuleResponseFields' RuleCreateProps: + discriminator: + propertyName: type anyOf: - $ref: '#/components/schemas/EqlRuleCreateProps' - $ref: '#/components/schemas/QueryRuleCreateProps' @@ -895,6 +896,8 @@ components: - $ref: '#/components/schemas/EsqlRuleCreateProps' RuleUpdateProps: + discriminator: + propertyName: type anyOf: - $ref: '#/components/schemas/EqlRuleUpdateProps' - $ref: '#/components/schemas/QueryRuleUpdateProps' @@ -917,6 +920,8 @@ components: - $ref: '#/components/schemas/EsqlRulePatchProps' RuleResponse: + discriminator: + propertyName: type anyOf: - $ref: '#/components/schemas/EqlRule' - $ref: '#/components/schemas/QueryRule' diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_actions/bulk_actions_route.gen.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_actions/bulk_actions_route.gen.ts index d11eea7b1671148..6d3594335e49b2b 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_actions/bulk_actions_route.gen.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_actions/bulk_actions_route.gen.ts @@ -105,35 +105,35 @@ export const BulkActionBase = z.object({ }); export type BulkDeleteRules = z.infer; -export const BulkDeleteRules = BulkActionBase.and( +export const BulkDeleteRules = BulkActionBase.merge( z.object({ action: z.literal('delete'), }) ); export type BulkDisableRules = z.infer; -export const BulkDisableRules = BulkActionBase.and( +export const BulkDisableRules = BulkActionBase.merge( z.object({ action: z.literal('disable'), }) ); export type BulkEnableRules = z.infer; -export const BulkEnableRules = BulkActionBase.and( +export const BulkEnableRules = BulkActionBase.merge( z.object({ action: z.literal('enable'), }) ); export type BulkExportRules = z.infer; -export const BulkExportRules = BulkActionBase.and( +export const BulkExportRules = BulkActionBase.merge( z.object({ action: z.literal('export'), }) ); export type BulkDuplicateRules = z.infer; -export const BulkDuplicateRules = BulkActionBase.and( +export const BulkDuplicateRules = BulkActionBase.merge( z.object({ action: z.literal('duplicate'), duplicate: z @@ -254,7 +254,7 @@ export const BulkActionEditPayload = z.union([ ]); export type BulkEditRules = z.infer; -export const BulkEditRules = BulkActionBase.and( +export const BulkEditRules = BulkActionBase.merge( z.object({ action: z.literal('edit'), /** diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_create_rules/bulk_create_rules_route.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_create_rules/bulk_create_rules_route.test.ts index 2e6cff31f8f7d51..52c7d5e097cc344 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_create_rules/bulk_create_rules_route.test.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_create_rules/bulk_create_rules_route.test.ts @@ -27,7 +27,7 @@ describe('Bulk create rules request schema', () => { const result = BulkCreateRulesRequestBody.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.name: Required, 0.description: Required, 0.risk_score: Required, 0.severity: Required, 0.type: Invalid literal value, expected \\"eql\\", and 52 more"` + `"0.type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'"` ); }); @@ -58,9 +58,7 @@ describe('Bulk create rules request schema', () => { const result = BulkCreateRulesRequestBody.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.risk_score: Required, 0.type: Invalid literal value, expected \\"eql\\", 0.language: Invalid literal value, expected \\"eql\\", 0.risk_score: Required, 0.risk_score: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toMatchInlineSnapshot(`"0.risk_score: Required"`); }); test('two array elements where the first is valid but the second is invalid (risk_score) will not validate', () => { @@ -72,9 +70,7 @@ describe('Bulk create rules request schema', () => { const result = BulkCreateRulesRequestBody.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"1.risk_score: Required, 1.type: Invalid literal value, expected \\"eql\\", 1.language: Invalid literal value, expected \\"eql\\", 1.risk_score: Required, 1.risk_score: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toMatchInlineSnapshot(`"1.risk_score: Required"`); }); test('two array elements where the first is invalid (risk_score) but the second is valid will not validate', () => { @@ -86,9 +82,7 @@ describe('Bulk create rules request schema', () => { const result = BulkCreateRulesRequestBody.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.risk_score: Required, 0.type: Invalid literal value, expected \\"eql\\", 0.language: Invalid literal value, expected \\"eql\\", 0.risk_score: Required, 0.risk_score: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toMatchInlineSnapshot(`"0.risk_score: Required"`); }); test('two array elements where both are invalid (risk_score) will not validate', () => { @@ -103,7 +97,7 @@ describe('Bulk create rules request schema', () => { const result = BulkCreateRulesRequestBody.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.risk_score: Required, 0.type: Invalid literal value, expected \\"eql\\", 0.language: Invalid literal value, expected \\"eql\\", 0.risk_score: Required, 0.risk_score: Required, and 49 more"` + `"0.risk_score: Required, 1.risk_score: Required"` ); }); @@ -130,7 +124,7 @@ describe('Bulk create rules request schema', () => { const result = BulkCreateRulesRequestBody.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'madeup', 0.type: Invalid literal value, expected \\"eql\\", 0.language: Invalid literal value, expected \\"eql\\", 0.severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'madeup', 0.severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'madeup', and 22 more"` + `"0.severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'madeup'"` ); }); @@ -165,7 +159,7 @@ describe('Bulk create rules request schema', () => { const result = BulkCreateRulesRequestBody.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.note: Expected string, received object, 0.type: Invalid literal value, expected \\"eql\\", 0.language: Invalid literal value, expected \\"eql\\", 0.note: Expected string, received object, 0.note: Expected string, received object, and 22 more"` + `"0.note: Expected string, received object"` ); }); }); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_update_rules/bulk_update_rules_route.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_update_rules/bulk_update_rules_route.test.ts index 3fa69c6ad24dcd7..f7e193856d0ea14 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_update_rules/bulk_update_rules_route.test.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/bulk_update_rules/bulk_update_rules_route.test.ts @@ -28,7 +28,7 @@ describe('Bulk update rules request schema', () => { const result = BulkUpdateRulesRequestBody.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.name: Required, 0.description: Required, 0.risk_score: Required, 0.severity: Required, 0.type: Invalid literal value, expected \\"eql\\", and 52 more"` + `"0.type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'"` ); }); @@ -59,9 +59,7 @@ describe('Bulk update rules request schema', () => { const result = BulkUpdateRulesRequestBody.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.risk_score: Required, 0.type: Invalid literal value, expected \\"eql\\", 0.language: Invalid literal value, expected \\"eql\\", 0.risk_score: Required, 0.risk_score: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toMatchInlineSnapshot(`"0.risk_score: Required"`); }); test('two array elements where the first is valid but the second is invalid (risk_score) will not validate', () => { @@ -73,9 +71,7 @@ describe('Bulk update rules request schema', () => { const result = BulkUpdateRulesRequestBody.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"1.risk_score: Required, 1.type: Invalid literal value, expected \\"eql\\", 1.language: Invalid literal value, expected \\"eql\\", 1.risk_score: Required, 1.risk_score: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toMatchInlineSnapshot(`"1.risk_score: Required"`); }); test('two array elements where the first is invalid (risk_score) but the second is valid will not validate', () => { @@ -87,9 +83,7 @@ describe('Bulk update rules request schema', () => { const result = BulkUpdateRulesRequestBody.safeParse(payload); expectParseError(result); - expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.risk_score: Required, 0.type: Invalid literal value, expected \\"eql\\", 0.language: Invalid literal value, expected \\"eql\\", 0.risk_score: Required, 0.risk_score: Required, and 22 more"` - ); + expect(stringifyZodError(result.error)).toMatchInlineSnapshot(`"0.risk_score: Required"`); }); test('two array elements where both are invalid (risk_score) will not validate', () => { @@ -104,7 +98,7 @@ describe('Bulk update rules request schema', () => { const result = BulkUpdateRulesRequestBody.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.risk_score: Required, 0.type: Invalid literal value, expected \\"eql\\", 0.language: Invalid literal value, expected \\"eql\\", 0.risk_score: Required, 0.risk_score: Required, and 49 more"` + `"0.risk_score: Required, 1.risk_score: Required"` ); }); @@ -131,7 +125,7 @@ describe('Bulk update rules request schema', () => { const result = BulkUpdateRulesRequestBody.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'madeup', 0.type: Invalid literal value, expected \\"eql\\", 0.language: Invalid literal value, expected \\"eql\\", 0.severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'madeup', 0.severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'madeup', and 22 more"` + `"0.severity: Invalid enum value. Expected 'low' | 'medium' | 'high' | 'critical', received 'madeup'"` ); }); @@ -176,7 +170,7 @@ describe('Bulk update rules request schema', () => { const result = BulkUpdateRulesRequestBody.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.note: Expected string, received object, 0.type: Invalid literal value, expected \\"eql\\", 0.language: Invalid literal value, expected \\"eql\\", 0.note: Expected string, received object, 0.note: Expected string, received object, and 22 more"` + `"0.note: Expected string, received object"` ); }); }); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/response_schema.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/response_schema.test.ts index 413d83f9fee019b..2d4af1c18f6d185 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/response_schema.test.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/bulk_crud/response_schema.test.ts @@ -46,7 +46,7 @@ describe('Bulk CRUD rules response schema', () => { const result = BulkCrudRulesResponse.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.error: Required, 0: Unrecognized key(s) in object: 'author', 'created_at', 'updated_at', 'created_by', 'description', 'enabled', 'false_positives', 'from', 'immutable', 'references', 'revision', 'severity', 'severity_mapping', 'updated_by', 'tags', 'to', 'threat', 'version', 'output_index', 'max_signals', 'risk_score', 'risk_score_mapping', 'interval', 'exceptions_list', 'related_integrations', 'required_fields', 'setup', 'throttle', 'actions', 'building_block_type', 'note', 'license', 'outcome', 'alias_target_id', 'alias_purpose', 'timeline_id', 'timeline_title', 'meta', 'rule_name_override', 'timestamp_override', 'timestamp_override_fallback_disabled', 'namespace', 'investigation_fields', 'query', 'type', 'language', 'index', 'data_view_id', 'filters', 'saved_id', 'response_actions', 'alert_suppression', 0.name: Required, 0.type: Invalid literal value, expected \\"eql\\", 0.language: Invalid literal value, expected \\"eql\\", and 24 more"` + `"0.name: Required, 0.error: Required, 0: Unrecognized key(s) in object: 'author', 'created_at', 'updated_at', 'created_by', 'description', 'enabled', 'false_positives', 'from', 'immutable', 'references', 'revision', 'severity', 'severity_mapping', 'updated_by', 'tags', 'to', 'threat', 'version', 'output_index', 'max_signals', 'risk_score', 'risk_score_mapping', 'interval', 'exceptions_list', 'related_integrations', 'required_fields', 'setup', 'throttle', 'actions', 'building_block_type', 'note', 'license', 'outcome', 'alias_target_id', 'alias_purpose', 'timeline_id', 'timeline_title', 'meta', 'rule_name_override', 'timestamp_override', 'timestamp_override_fallback_disabled', 'namespace', 'investigation_fields', 'query', 'type', 'language', 'index', 'data_view_id', 'filters', 'saved_id', 'response_actions', 'alert_suppression'"` ); }); @@ -59,7 +59,7 @@ describe('Bulk CRUD rules response schema', () => { const result = BulkCrudRulesResponse.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"0.error: Required, 0.name: Required, 0.description: Required, 0.risk_score: Required, 0.severity: Required, and 267 more"` + `"0.type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql', 0.error: Required"` ); }); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.test.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.test.ts index f53f67757ccdbda..3f364c6619db634 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.test.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.test.ts @@ -22,7 +22,7 @@ describe('RuleToImport', () => { expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"name: Required, description: Required, risk_score: Required, severity: Required, rule_id: Required, and 25 more"` + `"name: Required, description: Required, risk_score: Required, severity: Required, type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql', and 1 more"` ); }); @@ -47,7 +47,7 @@ describe('RuleToImport', () => { expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"name: Required, description: Required, risk_score: Required, severity: Required, type: Invalid literal value, expected \\"eql\\", and 24 more"` + `"name: Required, description: Required, risk_score: Required, severity: Required, type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'"` ); }); @@ -61,7 +61,7 @@ describe('RuleToImport', () => { expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"name: Required, risk_score: Required, severity: Required, type: Invalid literal value, expected \\"eql\\", query: Required, and 23 more"` + `"name: Required, risk_score: Required, severity: Required, type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'"` ); }); @@ -76,7 +76,7 @@ describe('RuleToImport', () => { expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"name: Required, risk_score: Required, severity: Required, type: Invalid literal value, expected \\"eql\\", query: Required, and 23 more"` + `"name: Required, risk_score: Required, severity: Required, type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql'"` ); }); @@ -330,7 +330,7 @@ describe('RuleToImport', () => { expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", index.0: Expected string, received number, index.0: Expected string, received number, type: Invalid literal value, expected \\"saved_query\\", and 20 more"` + `"index.0: Expected string, received number"` ); }); @@ -378,7 +378,7 @@ describe('RuleToImport', () => { expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", filters: Expected array, received string, filters: Expected array, received string, type: Invalid literal value, expected \\"saved_query\\", and 20 more"` + `"filters: Expected array, received string"` ); }); @@ -414,7 +414,7 @@ describe('RuleToImport', () => { expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", language: Invalid enum value. Expected 'kuery' | 'lucene', received 'something-made-up', type: Invalid literal value, expected \\"saved_query\\", saved_id: Required, and 19 more"` + `"language: Invalid enum value. Expected 'kuery' | 'lucene', received 'something-made-up'"` ); }); diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.ts b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.ts index 5c9514943ac4114..9634d773b121dce 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.ts +++ b/x-pack/plugins/security_solution/common/api/detection_engine/rule_management/import_rules/rule_to_import.ts @@ -8,7 +8,7 @@ import * as z from 'zod'; import { BaseCreateProps, - ResponseRequiredFields, + ResponseFields, RuleSignatureId, TypeSpecificCreateProps, } from '../../model/rule_schema'; @@ -26,7 +26,7 @@ import { export type RuleToImport = z.infer; export type RuleToImportInput = z.input; export const RuleToImport = BaseCreateProps.and(TypeSpecificCreateProps).and( - ResponseRequiredFields.partial().extend({ + ResponseFields.partial().extend({ rule_id: RuleSignatureId, immutable: z.literal(false).default(false), }) diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/execute.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/execute.gen.ts index dbd24eef454d311..12a227048d33d8c 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/execute.gen.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/execute.gen.ts @@ -15,7 +15,7 @@ import { z } from 'zod'; import { BaseActionSchema, Command, Timeout } from '../model/schema/common.gen'; export type ExecuteActionRequestBody = z.infer; -export const ExecuteActionRequestBody = BaseActionSchema.and( +export const ExecuteActionRequestBody = BaseActionSchema.merge( z.object({ parameters: z.object({ command: Command, diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_upload.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/file_upload.gen.ts index 785a4a1097e0cd2..4f40d187e7e3e50 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/file_upload.gen.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/file_upload.gen.ts @@ -15,7 +15,7 @@ import { z } from 'zod'; import { BaseActionSchema } from '../model/schema/common.gen'; export type FileUploadActionRequestBody = z.infer; -export const FileUploadActionRequestBody = BaseActionSchema.and( +export const FileUploadActionRequestBody = BaseActionSchema.merge( z.object({ parameters: z.object({ overwrite: z.boolean().optional().default(false), diff --git a/x-pack/plugins/security_solution/common/api/endpoint/actions/get_file.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/actions/get_file.gen.ts index 648f1700a54ca1b..1b1513af9b13b5e 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/actions/get_file.gen.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/actions/get_file.gen.ts @@ -15,7 +15,7 @@ import { z } from 'zod'; import { BaseActionSchema } from '../model/schema/common.gen'; export type GetFileActionRequestBody = z.infer; -export const GetFileActionRequestBody = BaseActionSchema.and( +export const GetFileActionRequestBody = BaseActionSchema.merge( z.object({ parameters: z.object({ path: z.string(), diff --git a/x-pack/plugins/security_solution/common/api/endpoint/model/schema/common.gen.ts b/x-pack/plugins/security_solution/common/api/endpoint/model/schema/common.gen.ts index 986260c2e463feb..c5fc0f38f6b05e5 100644 --- a/x-pack/plugins/security_solution/common/api/endpoint/model/schema/common.gen.ts +++ b/x-pack/plugins/security_solution/common/api/endpoint/model/schema/common.gen.ts @@ -145,7 +145,7 @@ export const BaseActionSchema = z.object({ }); export type ProcessActionSchemas = z.infer; -export const ProcessActionSchemas = BaseActionSchema.and( +export const ProcessActionSchemas = BaseActionSchema.merge( z.object({ parameters: z.union([ z.object({ diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx index dc762337f5e0efa..282d3fcc8439a57 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx @@ -25,6 +25,7 @@ import type { Threats, } from '@kbn/securitysolution-io-ts-alerting-types'; import { ALERT_RISK_SCORE } from '@kbn/rule-data-utils'; +import { requiredOptional } from '@kbn/zod-helpers'; import type { RuleResponse } from '../../../../../common/api/detection_engine/model/rule_schema'; import { SeverityBadge } from '../../../../detections/components/rules/severity_badge'; import { defaultToEmptyTag } from '../../../../common/components/empty_value'; @@ -333,7 +334,9 @@ const prepareAboutSectionListItems = ( ) : ( '' ), - description: , + description: ( + + ), }; }) ); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx index 3acccc3352a4144..072649d52a6a6af 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx @@ -21,6 +21,7 @@ import type { import { ENDPOINT_LIST_ID } from '@kbn/securitysolution-list-constants'; import type { Filter } from '@kbn/es-query'; import type { ActionVariables } from '@kbn/triggers-actions-ui-plugin/public'; +import { requiredOptional } from '@kbn/zod-helpers'; import type { ResponseAction } from '../../../../../common/api/detection_engine/model/rule_response_actions'; import { normalizeThresholdField } from '../../../../../common/detection_engine/utils'; import { assertUnreachable } from '../../../../../common/utility_types'; @@ -253,7 +254,7 @@ export const getAboutStepsData = (rule: RuleResponse, detailsView: boolean): Abo tags, riskScore: { value: riskScore, - mapping: riskScoreMapping, + mapping: requiredOptional(riskScoreMapping), isMappingChecked: riskScoreMapping.length > 0, }, falsePositives, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/convert_rule_to_diffable.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/convert_rule_to_diffable.ts index e41014650554501..95a8687324d1caa 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/convert_rule_to_diffable.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/logic/diff/normalization/convert_rule_to_diffable.ts @@ -6,6 +6,7 @@ */ import type { RuleActionArray } from '@kbn/securitysolution-io-ts-alerting-types'; +import { requiredOptional } from '@kbn/zod-helpers'; import { DEFAULT_MAX_SIGNALS } from '../../../../../../../common/constants'; import { assertUnreachable } from '../../../../../../../common/utility_types'; import type { @@ -123,7 +124,7 @@ const extractDiffableCommonFields = ( severity: rule.severity, severity_mapping: rule.severity_mapping ?? [], risk_score: rule.risk_score, - risk_score_mapping: rule.risk_score_mapping ?? [], + risk_score_mapping: rule.risk_score_mapping?.map((mapping) => requiredOptional(mapping)) ?? [], // About -> Advanced settings references: rule.references ?? [], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.test.ts index 0ec1d5580f40b05..06ccf0a2b97f4a0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/model/rule_assets/prebuilt_rule_asset.test.ts @@ -17,7 +17,7 @@ describe('Prebuilt rule asset schema', () => { const result = PrebuiltRuleAsset.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"name: Required, description: Required, risk_score: Required, severity: Required, rule_id: Required, and 26 more"` + `"name: Required, description: Required, risk_score: Required, severity: Required, type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql', and 2 more"` ); }); @@ -40,7 +40,7 @@ describe('Prebuilt rule asset schema', () => { const result = PrebuiltRuleAsset.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"name: Required, description: Required, risk_score: Required, severity: Required, version: Required, and 25 more"` + `"name: Required, description: Required, risk_score: Required, severity: Required, type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql', and 1 more"` ); }); @@ -177,7 +177,7 @@ describe('Prebuilt rule asset schema', () => { const result = PrebuiltRuleAsset.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", index.0: Expected string, received number, index.0: Expected string, received number, type: Invalid literal value, expected \\"saved_query\\", and 20 more"` + `"index.0: Expected string, received number"` ); }); @@ -201,7 +201,7 @@ describe('Prebuilt rule asset schema', () => { const result = PrebuiltRuleAsset.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", filters: Expected array, received string, filters: Expected array, received string, type: Invalid literal value, expected \\"saved_query\\", and 20 more"` + `"filters: Expected array, received string"` ); }); @@ -236,7 +236,7 @@ describe('Prebuilt rule asset schema', () => { const result = PrebuiltRuleAsset.safeParse(payload); expectParseError(result); expect(stringifyZodError(result.error)).toMatchInlineSnapshot( - `"type: Invalid literal value, expected \\"eql\\", language: Invalid literal value, expected \\"eql\\", language: Invalid enum value. Expected 'kuery' | 'lucene', received 'something-made-up', type: Invalid literal value, expected \\"saved_query\\", saved_id: Required, and 19 more"` + `"language: Invalid enum value. Expected 'kuery' | 'lucene', received 'something-made-up'"` ); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.test.ts index a1d74b14455081f..f45f005c7c34b84 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/create_rule/route.test.ts @@ -237,7 +237,7 @@ describe('Create rule route', () => { }); const result = await server.validate(request); expect(result.badRequest).toHaveBeenCalledWith( - 'type: Invalid literal value, expected "eql", language: Invalid literal value, expected "eql", type: Invalid literal value, expected "saved_query", saved_id: Required, type: Invalid literal value, expected "threshold", and 18 more' + 'response_actions.0.action_type_id: Invalid literal value, expected ".osquery", response_actions.0.params.command: Invalid literal value, expected "isolate"' ); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.test.ts index f95b10fa6154f69..6bdafa76fd9b670 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/rules/update_rule/route.test.ts @@ -284,7 +284,7 @@ describe('Update rule route', () => { }); const result = await server.validate(request); expect(result.badRequest).toHaveBeenCalledWith( - 'type: Invalid literal value, expected "eql", language: Invalid literal value, expected "eql", type: Invalid literal value, expected "saved_query", saved_id: Required, type: Invalid literal value, expected "threshold", and 18 more' + `response_actions.0.action_type_id: Invalid literal value, expected \".osquery\", response_actions.0.params.command: Invalid literal value, expected \"isolate\"` ); }); }); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/create_rules_stream_from_ndjson.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/create_rules_stream_from_ndjson.test.ts index be9561598fd0847..b6cffd47a494e88 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/create_rules_stream_from_ndjson.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/logic/import/create_rules_stream_from_ndjson.test.ts @@ -283,7 +283,7 @@ describe('create_rules_stream_from_ndjson', () => { immutable: false, }); expect(resultOrError[1].message).toContain( - 'name: Required, description: Required, risk_score: Required, severity: Required, rule_id: Required, and 25 more' + `name: Required, description: Required, risk_score: Required, severity: Required, type: Invalid discriminator value. Expected 'eql' | 'query' | 'saved_query' | 'threshold' | 'threat_match' | 'machine_learning' | 'new_terms' | 'esql', and 1 more` ); expect(resultOrError[2]).toEqual({ rule_id: 'rule-2', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts index a513e8468d57739..8fa275c7ba59f6c 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/utils/validate.test.ts @@ -95,7 +95,7 @@ describe('validate', () => { delete ruleAlert.name; expect(() => { transformValidate(ruleAlert); - }).toThrowError('Invalid input'); + }).toThrowError('Required'); }); }); @@ -113,8 +113,7 @@ describe('validate', () => { const validatedOrError = transformValidateBulkError('rule-1', ruleAlert); const expected: BulkError = { error: { - message: - 'name: Required, type: Invalid literal value, expected "eql", language: Invalid literal value, expected "eql", name: Required, name: Required, and 22 more', + message: 'name: Required', status_code: 500, }, rule_id: 'rule-1', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/osquery_response_action.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/osquery_response_action.ts index c8dfa113af6a4a6..5852e6f964c7bae 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/osquery_response_action.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_response_actions/osquery_response_action.ts @@ -7,6 +7,7 @@ import { each, map, some, uniq } from 'lodash'; import { containsDynamicQuery } from '@kbn/osquery-plugin/common/utils/replace_params_query'; +import { requiredOptional } from '@kbn/zod-helpers'; import type { ResponseActionAlerts } from './types'; import type { SetupPlugins } from '../../../plugin_contract'; import type { RuleResponseOsqueryAction } from '../../../../common/api/detection_engine/model/rule_response_actions'; @@ -32,7 +33,7 @@ export const osqueryResponseAction = ( return osqueryCreateActionService.create({ ...rest, - queries, + queries: requiredOptional(queries), ecs_mapping: ecsMapping, saved_query_id: savedQueryId, agent_ids: agentIds, @@ -43,7 +44,7 @@ export const osqueryResponseAction = ( return osqueryCreateActionService.create( { ...rest, - queries, + queries: requiredOptional(queries), ecs_mapping: ecsMapping, saved_query_id: savedQueryId, agent_ids: alert.agent?.id ? [alert.agent.id] : [], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts index 2309833a947f001..7641c71b28dbfa7 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_alert.ts @@ -43,6 +43,7 @@ import { TIMESTAMP, } from '@kbn/rule-data-utils'; import { flattenWithPrefix } from '@kbn/securitysolution-rules'; +import { requiredOptional } from '@kbn/zod-helpers'; import { createHash } from 'crypto'; @@ -229,7 +230,7 @@ export const buildAlert = ( [ALERT_RULE_NAMESPACE_FIELD]: params.namespace, [ALERT_RULE_NOTE]: params.note, [ALERT_RULE_REFERENCES]: params.references, - [ALERT_RULE_RISK_SCORE_MAPPING]: params.riskScoreMapping, + [ALERT_RULE_RISK_SCORE_MAPPING]: requiredOptional(params.riskScoreMapping), [ALERT_RULE_RULE_ID]: params.ruleId, [ALERT_RULE_RULE_NAME_OVERRIDE]: params.ruleNameOverride, [ALERT_RULE_SEVERITY_MAPPING]: params.severityMapping, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts index 50df9b714c3cae3..fb1aa57fdb82d3b 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/factories/utils/build_bulk_body.ts @@ -7,6 +7,7 @@ import { flattenWithPrefix } from '@kbn/securitysolution-rules'; import type * as estypes from '@elastic/elasticsearch/lib/api/types'; +import { requiredOptional } from '@kbn/zod-helpers'; import type { BaseHit, SearchTypes } from '../../../../../../common/detection_engine/types'; import type { ConfigType } from '../../../../../config'; @@ -92,7 +93,7 @@ export const buildBulkBody = ( riskScoreOverride: buildRiskScoreFromMapping({ eventSource: mergedDoc._source ?? {}, riskScore: completeRule.ruleParams.riskScore, - riskScoreMapping: completeRule.ruleParams.riskScoreMapping, + riskScoreMapping: requiredOptional(completeRule.ruleParams.riskScoreMapping), }).riskScore, } : undefined; diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules_bulk.ts index 6404da38cdde769..982557130717ecc 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group1/create_rules_bulk.ts @@ -447,7 +447,7 @@ export default ({ getService }: FtrProviderContext): void => { .expect(400); expect(body.message).to.eql( - '[request body]: 0.investigation_fields: Expected object, received array, 0.type: Invalid literal value, expected "eql", 0.language: Invalid literal value, expected "eql", 0.investigation_fields: Expected object, received array, 0.investigation_fields: Expected object, received array, and 22 more' + '[request body]: 0.investigation_fields: Expected object, received array' ); }); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_rules.ts index f8dbcffad3ba48c..6517c46bddcaf24 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/import_rules.ts @@ -411,11 +411,7 @@ export default ({ getService }: FtrProviderContext): void => { expect(body.errors[0]).to.eql({ rule_id: '(unknown id)', - error: { - message: - 'type: Invalid literal value, expected "eql", language: Invalid literal value, expected "eql", type: Invalid literal value, expected "query", type: Invalid literal value, expected "saved_query", saved_id: Required, and 14 more', - status_code: 400, - }, + error: { status_code: 400, message: 'threshold: Required' }, }); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts index d88ed8a898f9061..afb0205ce458f9c 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules.ts @@ -566,8 +566,7 @@ export default ({ getService }: FtrProviderContext) => { expect(body).to.eql({ error: 'Bad Request', - message: - '[request body]: type: Invalid literal value, expected "eql", language: Invalid literal value, expected "eql", type: Invalid literal value, expected "query", type: Invalid literal value, expected "saved_query", saved_id: Required, and 14 more', + message: '[request body]: threshold: Required', statusCode: 400, }); }); @@ -957,7 +956,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(400); expect(body.message).to.eql( - '[request body]: investigation_fields: Expected object, received array, type: Invalid literal value, expected "eql", language: Invalid literal value, expected "eql", investigation_fields: Expected object, received array, investigation_fields: Expected object, received array, and 22 more' + '[request body]: investigation_fields: Expected object, received array' ); }); diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts index a3defbb6d1c827f..569069cee306234 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/group10/update_rules_bulk.ts @@ -854,7 +854,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(400); expect(body.message).to.eql( - '[request body]: 0.investigation_fields: Expected object, received array, 0.type: Invalid literal value, expected "eql", 0.language: Invalid literal value, expected "eql", 0.investigation_fields: Expected object, received array, 0.investigation_fields: Expected object, received array, and 22 more' + '[request body]: 0.investigation_fields: Expected object, received array' ); }); diff --git a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_creation/create_rules.ts b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_creation/create_rules.ts index a887f07cb2cfe59..49ed77a4dc48ec4 100644 --- a/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_creation/create_rules.ts +++ b/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_creation/create_rules.ts @@ -431,8 +431,7 @@ export default ({ getService }: FtrProviderContext) => { expect(body).toEqual({ error: 'Bad Request', - message: - '[request body]: type: Invalid literal value, expected "eql", language: Invalid literal value, expected "eql", type: Invalid literal value, expected "query", type: Invalid literal value, expected "saved_query", saved_id: Required, and 14 more', + message: '[request body]: threshold: Required', statusCode: 400, }); }); @@ -541,7 +540,7 @@ export default ({ getService }: FtrProviderContext) => { .expect(400); expect(body.message).toBe( - '[request body]: investigation_fields: Expected object, received array, type: Invalid literal value, expected "eql", language: Invalid literal value, expected "eql", investigation_fields: Expected object, received array, investigation_fields: Expected object, received array, and 22 more' + '[request body]: investigation_fields: Expected object, received array' ); }); });