diff --git a/src/main.ts b/src/main.ts index df502868..c5316a9b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,7 +3,7 @@ import { v4 as uuidv4 } from 'uuid'; import { logger } from '@/logger.js'; import { - stixBundleSchema, + stixBundleBaseSchema, type AttackObject, type StixBundle, } from './schemas/sdo/stix-bundle.schema.js'; @@ -202,7 +202,7 @@ function parseStixBundle(rawData: StixBundle, parsingMode: ParsingMode): AttackO const validObjects: AttackObject[] = []; // Validate the bundle's top-level properties - const baseBundleValidationResults = stixBundleSchema + const baseBundleValidationResults = stixBundleBaseSchema .pick({ id: true, type: true, diff --git a/src/schemas/sdo/stix-bundle.schema.ts b/src/schemas/sdo/stix-bundle.schema.ts index 1729ec21..da978b24 100644 --- a/src/schemas/sdo/stix-bundle.schema.ts +++ b/src/schemas/sdo/stix-bundle.schema.ts @@ -160,7 +160,10 @@ export type AttackObjects = z.infer; // //============================================================================== -export const stixBundleSchema = z +// Base schema without refinements so it can be composed with `.pick()`, `.extend()`, etc. +// Zod v4 forbids `.pick()` on schemas containing refinements (`.check()`), so the +// refinements below are attached to `stixBundleSchema` rather than this base. +export const stixBundleBaseSchema = z .object({ id: createStixIdValidator('bundle'), type: createStixTypeValidator('bundle'), @@ -190,20 +193,21 @@ export const stixBundleSchema = z description: 'A Bundle is a collection of arbitrary STIX Objects grouped together in a single container. A Bundle does not have any semantic meaning and the objects contained within the Bundle are not considered related by virtue of being in the same Bundle. A STIX Bundle Object is not a STIX Object but makes use of the type and id Common Properties.', }) - .strict() - .check((ctx) => { - // Validate that the first object is 'x-mitre-collection' and only one collection exists - validateXMitreCollection()(ctx); + .strict(); - // Validate that all IDs referenced in 'x_mitre_contents' are present in 'objects' array - validateXMitreContentsReferences()(ctx); +export const stixBundleSchema = stixBundleBaseSchema.check((ctx) => { + // Validate that the first object is 'x-mitre-collection' and only one collection exists + validateXMitreCollection()(ctx); - // Validate that no duplicate objects are present in 'objects' array - validateNoDuplicates( - ['objects'], - ['id'], - 'Duplicate object with id "{id}" found. Each object in the bundle must have a unique id.', - )(ctx); - }); + // Validate that all IDs referenced in 'x_mitre_contents' are present in 'objects' array + validateXMitreContentsReferences()(ctx); + + // Validate that no duplicate objects are present in 'objects' array + validateNoDuplicates( + ['objects'], + ['id'], + 'Duplicate object with id "{id}" found. Each object in the bundle must have a unique id.', + )(ctx); +}); export type StixBundle = z.infer;