Skip to content

Sibling required keyword is dropped when a property uses $ref to an external schema #866

Description

@evanmarshall

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

  1. Register an external Foo schema that requires value.
  2. Create a root schema with a property x that uses $ref: Foo plus a sibling
    required: ['extra'].
  3. Build the serializer with Foo registered as an external schema.
  4. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions