ajv-validator / ajv-validator/ajv

oneOf incorrectly tracks evaluated properties for unevaluatedProperties (Draft 2020-12)

Aperta
#2,579 5 commenti 0 reazioni 0 assegnatari Vedi su GitHub
Lingua principale
TypeScript
Stelle
14.8k
Fork
1k
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Descrizione

🔴 Summary

When using oneOf together with unevaluatedProperties (Draft 2020-12), Ajv only merges evaluation information from the subschema that passes validation.
Properties (or items) that are processed by oneOf branches which fail validation are not recorded as evaluated, which can cause unevaluatedProperties: false to incorrectly reject valid instances.

📜 Spec reference

JSON Schema Draft 2020-12, §6.5.3.2.2 (Evaluation of instance locations):

Instance locations are considered evaluated when they are processed by an applicable keyword, regardless of whether validation of that keyword succeeds or fails.

Under this model, evaluation is tied to processing, not to overall subschema validity in oneOf.

📍 Location in code
lib/vocabularies/applicator/oneOf.ts

Specifically, cxt.mergeEvaluated(...) is currently called only when schValid === true, so evaluated locations from failing oneOf branches are dropped.

🧪 Minimal reproduction

Schema (Draft 2020-12):

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"oneOf": [
{
"properties": { "a": { "type": "string" } },
"required": ["a", "x"]
},
{
"properties": { "b": { "type": "number" } },
"required": ["b"]
}
],
"unevaluatedProperties": false
}

Instance:

{ "a": "test", "b": 42 }

❌ Current behavior

Subschema 0 evaluates property "a" but fails overall (missing "x").

Subschema 1 evaluates property "b" and passes.

Ajv merges evaluated properties only from the passing subschema.

Property "a" is treated as unevaluated, causing a validation failure.

✅ Expected behavior

Both "a" and "b" should be considered evaluated, since they were processed by applicable keywords in oneOf subschemas.
The instance should validate successfully, and only truly unevaluated properties (e.g. "c") should be rejected.

💥 Impact

Causes false negatives when combining oneOf with unevaluatedProperties.

Affects common real-world patterns (union-like object schemas in APIs).

Violates Draft 2020-12 evaluation semantics.

🔧 Suggested fix (high-level)

Merge evaluated properties/items from all evaluated oneOf subschemas, regardless of validation outcome, while keeping the existing oneOf requirement that exactly one subschema must pass.
Since mergeEvaluated is already gated by opts.unevaluated, this should not affect drafts or configurations where unevaluated tracking is disabled.

📝 Notes

I have a local patch and regression tests that address this issue and am happy to open a PR if the approach above aligns with Ajv’s expectations.

Guida per i contributori

Apri la guida per i contributori

Valutazione

Questa issue non è ancora stata valutata.

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.