What's wrong
When a property both references an external schema with $ref and adds its own
sibling keywords (like required), the serializer throws away those sibling
keywords. An object that is missing a required field is accepted and serialized
as valid instead of raising a validation error, so incomplete data passes
through silently.
Where
index.js, in buildValue:
https://github.com/fastify/fast-json-stringify/blob/7fad3c2d6305/index.js#L1300-L1303
if (schema.$ref) {
location = resolveRef(context, location)
schema = location.schema
}
Resolving the reference replaces the local schema wholesale, so any sibling
keywords on the referencing schema (for example required) are gone before the
serializer is generated. The getValidatorSchemaRef guard added recently
(index.js:113, treats a schema with more than one key as non-pure) correctly
protects validator reference selection in buildOneOf/buildIfThenElse, but it
does not stop buildValue from dereferencing and discarding siblings.
How it manifests
- Register an external
Foo schema that requires value.
- Create a root schema with a property
x that uses $ref: Foo plus a sibling
required: ['extra'].
- Build the serializer with
Foo registered as an external schema.
- Serialize
{ x: { value: 1, extra: "x" } } and then { x: { value: 1 } }.
Both inputs serialize successfully, even though the second is missing the
sibling-required extra.
Expected behavior
The sibling required constraint should still apply. When a $ref has sibling
keywords, resolve the reference as the base schema and then apply the sibling
constraints on top, keeping the direct-dereference optimization only for a
schema that contains $ref alone.
Found while running Ito (AI code review, free for open source) against recently merged PRs. Full analysis: https://app.ito.ai/share/29ad67b0-500d-45d1-9ffd-df265504fb5f.
What's wrong
When a property both references an external schema with
$refand adds its ownsibling keywords (like
required), the serializer throws away those siblingkeywords. An object that is missing a required field is accepted and serialized
as valid instead of raising a validation error, so incomplete data passes
through silently.
Where
index.js, inbuildValue:https://github.com/fastify/fast-json-stringify/blob/7fad3c2d6305/index.js#L1300-L1303
Resolving the reference replaces the local schema wholesale, so any sibling
keywords on the referencing schema (for example
required) are gone before theserializer is generated. The
getValidatorSchemaRefguard added recently(
index.js:113, treats a schema with more than one key as non-pure) correctlyprotects validator reference selection in
buildOneOf/buildIfThenElse, but itdoes not stop
buildValuefrom dereferencing and discarding siblings.How it manifests
Fooschema that requiresvalue.xthat uses$ref: Fooplus a siblingrequired: ['extra'].Fooregistered as an external schema.{ x: { value: 1, extra: "x" } }and then{ x: { value: 1 } }.Both inputs serialize successfully, even though the second is missing the
sibling-required
extra.Expected behavior
The sibling
requiredconstraint should still apply. When a$refhas siblingkeywords, resolve the reference as the base schema and then apply the sibling
constraints on top, keeping the direct-dereference optimization only for a
schema that contains
$refalone.Found while running Ito (AI code review, free for open source) against recently merged PRs. Full analysis: https://app.ito.ai/share/29ad67b0-500d-45d1-9ffd-df265504fb5f.