Skip to content

fix: resolve percent-encoded $ref definition keys#846

Open
DucMinhNe wants to merge 1 commit into
fastify:mainfrom
DucMinhNe:fix/percent-encoded-ref-keys
Open

fix: resolve percent-encoded $ref definition keys#846
DucMinhNe wants to merge 1 commit into
fastify:mainfrom
DucMinhNe:fix/percent-encoded-ref-keys

Conversation

@DucMinhNe

Copy link
Copy Markdown

Fixes #740

Problem

When a definition key is itself percent-encoded (e.g. Some%3Cloremipsum%3E) and the referenced subschema contains oneOf/anyOf/allOf, building a serializer throws:

Error: can't resolve reference #/definitions/Some%3Cloremipsum%3E from id __fjs_root_0

Such schemas need Ajv to compile a sub-validator for the oneOf/anyOf/allOf branch. Ajv resolves a $ref JSON pointer by url-decoding each segment, so #/definitions/Some%3Cloremipsum%3E decodes to Some<loremipsum>, which does not match the literal definition key Some%3Cloremipsum%3E — hence MissingRefError. Plain refs (no oneOf/anyOf/allOf) avoided this only because they were resolved by the internal literal-lookup resolver instead of Ajv.

Fix

In lib/validator.js, re-encode the % in the ref fragment before handing it to Ajv (in validate() and when converting a schema's $ref in convertSchemaToAjvFormat). Ajv's single decode then round-trips back to the original literal key. The internal json-schema-ref-resolver used by the serializer (which performs a literal lookup) is untouched, so both resolvers stay correct.

The fix is ~12 lines and adds a small escapeRefForAjv helper.

Test

Added a unit test in test/ref.test.js (node:test, matching the existing file style) that ports the repro from the issue: a schema with a percent-encoded $ref definition key whose subschema uses oneOf now serializes correctly.

Full suite is green: npm test → 473 unit tests + tstyche type tests pass, lint clean.

Note

Standalone mode (mode: 'standalone') carries the same escaped refs through restoreFromState, so it benefits from this fix as well; any remaining standalone-specific edge cases are pre-existing and out of scope for this change.

Ajv resolves a $ref's JSON pointer by url-decoding each segment, so a
definition whose key is itself percent-encoded (e.g. Some%3Cloremipsum%3E)
could no longer be found once the referenced schema contained
oneOf/anyOf/allOf and Ajv compiled a sub-validator for it: the pointer
#/definitions/Some%3Cloremipsum%3E decodes to Some<loremipsum>, which
does not match the literal key, throwing MissingRefError.

Re-encode the percent signs in the ref fragment before handing it to Ajv
so its single decode round-trips back to the original key. The internal
json-schema-ref-resolver (which performs a literal lookup) is untouched.

Fixes fastify#740

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a $ref resolution failure when a schema definition key is itself percent-encoded (e.g. Some%3Cloremipsum%3E) and Ajv is invoked to compile/validate oneOf/anyOf/allOf branches (issue #740). It does so by escaping % in the $ref fragment before Ajv processes it, so Ajv’s single decode round-trips back to the original literal key.

Changes:

  • Add an escapeRefForAjv helper and apply it to schema refs passed into Validator.validate().
  • Escape $ref strings during Ajv schema conversion (convertSchemaToAjvFormat).
  • Add a regression test covering a percent-encoded definition key referenced from a schema that uses oneOf.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
lib/validator.js Escapes % in $ref fragments for Ajv validation and during Ajv schema conversion to avoid percent-decoding mismatches.
test/ref.test.js Adds a regression test reproducing issue #740 with a percent-encoded definition key and oneOf.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/validator.js
Comment on lines +74 to +76
if (typeof schema.$ref === 'string') {
schema.$ref = escapeRefForAjv(schema.$ref)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Error using stringify on schema with uri-encoded definition property containing anyOf, allOf or oneOf

2 participants