Skip to main content

Module forbidden_scan

Module forbidden_scan 

Source
Expand description

Recursive forbidden meta-key scan (SPEC §6) — the runtime half of the global rejection that the meta-schema’s propertyNames enforces statically.

R1: the types/parser PRESERVE every key of the open buckets (round-trip), so blocking forbidden meta keys is the VALIDATION layer’s job, not the model’s. The typed models (FieldSpec + ExtraMap) only reject forbidden keys at the top level, as unknown keys inside the closed buckets, and one level under open buckets — a deeply nested meta key (validate.required.if, options.x.display_switch, …) leaks through. This scan closes that leak: it walks the COMPOSED single spec (after $ref/$patch expansion and x-strip) to ARBITRARY depth and rejects a forbidden key found at ANY depth — including one level under a slot/bucket body.

Placement (SPEC §2 pipeline): this runs in the spec LOAD path, immediately after compose expansion and before validation entry. A hit is therefore a LOAD failure (ComposeLoadError, code FORBIDDEN_META_KEY) — the spec never comes into existence — never a valid:false validation result.

Forbidden set (SPEC §6): the enumerated FORBIDDEN_META_KEYS (condition-only / retired / magic-symbol meta keys) PLUS the x{key} comment family (any x-prefixed key). $ref/$patch are NOT forbidden — compose already consumed them, so they do not survive to here; x{key} IS strip- eligible, so any x{key} that survives to this scan is rejected (the strip belongs to the meta-schema; survival means it was not stripped).

Byte-for-byte with the JS reference (packages/validator/ts/src/ forbidden-scan.ts); the shared 4-language fixture tests/fixtures/spec-validity/cases.json is the single source of truth.

Functions§

scan_forbidden_keys
Recursively scan a composed single spec for any forbidden meta key at any depth. Returns Err(ComposeLoadError { code: FORBIDDEN_META_KEY }) on the first hit, with the dotted path to the offending key in message and trace.