Skip to content

Move fast-uri to 3.1.9 - #38

Merged
threeqube merged 1 commit into
mainfrom
fast-uri-3.1.9
Oct 29, 2026
Merged

threeqube merged 1 commit into
mainfrom
fast-uri-3.1.9

Conversation

@threeqube

@threeqube threeqube commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

Part of #37 (PR 1 of 3). It leaves #37 open: the @stablelib/ed25519 alert stays open until digitalcredentials/verifier-core#84 is resolved.

What changes

package-lock.json only: fast-uri 3.1.8 → 3.1.9, three lines (version, address, checksum). package.json is untouched, because 3.1.9 is within the ^3.0.1 that ajv asks for. 3.1.9 was published on 19 October 2026 by fast-uri's own GitHub Actions, with npm provenance (built from fastify/fast-uri a1d0da2 on v3.x; npm's gitHead, 0def703, is the bot's version bump on top of it). The advisories were published on 23 October, after the fix.

This should close the four fast-uri alerts (GHSA-w74x-v6f9-j3xg, GHSA-wjr9-j795-jxcr, GHSA-xhq7-xmqj-769h, GHSA-333x-w4r6-6crp). fast-uri is the only package bundled into the plugin that has a fix in #37: it reaches dist/ through verifier-core → ajv.

The built plugin differs only in fast-uri (verified)

Built at main (d575001) and at this branch, then compared:

  • dist/index.js is byte-for-byte identical outside fast-uri's three bundled modules (index.js, lib/utils.js, lib/schemes.js). Upstream, 3.1.9 changes index.js and lib/utils.js and leaves lib/schemes.js alone, and the bundled lib/schemes.js is unchanged too.
  • dist/THIRD_PARTY_LICENSES.md changes one line: fast-uri - 3.1.8 becomes 3.1.9.
  • No other file in dist/ changes.

What was exposed, and why it's low

All four advisories describe the same situation: an app gives fast-uri an untrusted address, then trusts what comes back (a %6A-encoded javascript: decoded into a live one, a host silently rewritten, leading .. segments dropped, a colon left unescaped).

Verified, by wrapping fast-uri's functions and recording every call, with its answer, while credentials were checked in Node (both through verifier-core directly and through the built plugin's own verify()):

  • No fast-uri call happens while a credential is being checked, whatever it contains. fast-uri is called only while ajv compiles a schema, and only on schema addresses.
  • In the built plugin, the only caller of fast-uri is ajv (through ajv/dist/runtime/uri), and ajv is used only by verifier-core's Open Badges schema check.
  • That check compiles the Open Badges schemas bundled with verifier-core. It never fetches a schema, and the credential's own credentialSchema doesn't choose one. Every fast-uri call was on a schema address (the json-schema.org metaschemas and the bundled purl.imsglobal.org schemas): 306 calls for one credential, 800 when compiling all four bundled schemas, and none carried anything from a credential. That held even when a hostile credential was checked first, before ajv had compiled anything.
  • Examples tried, with the advisories' inputs and close variants (an @-smuggled host, a %6A-encoded scheme, leading .. segments, a tab in the host, a bare javascript:):
    • In the credential's web addresses (issuer url and image, achievement id, image and criteria, a declared credentialSchema), signed by a demo issuer: where the credential got as far as the schema check, the check ran and fast-uri was not called. The %6A, .. and tab forms never get that far: the signing library refuses them, and signed with its safe mode off, verifier-core rejects them first (its structure check, or the signature check).
    • In plain-text fields (name, description): the schema check ran on all of them, and fast-uri was not called.
  • On 3.1.8 and 3.1.9, every one of those calls gets the same answer, so this changes nothing about how the bundled schemas resolve.

A correction to #37, which said a credential's web addresses are checked by a regular expression in ajv-formats. They aren't: none of the four bundled schemas uses format: uri (only date and date-time), so url and the ids are checked only as strings. Neither fast-uri nor that regular expression sees them. #37 is corrected to match.

So the fix is worth taking, and we found no way for a credential to reach the vulnerable code.

Checks

  • Lint, typecheck, 105,720 unit tests, and 60 browser tests (1 skipped: it runs only against a published copy, which CI's preview job does). Run locally on Node 26; CI runs Node 24.
  • Reviewed independently: a separate build of both commits, each claim above rechecked by running it.
  • Smoke test, comparing the live demo (main) with this PR's preview, using a local compare script that clicks every demo button and opens 9 vc-test-fixtures links at phone width. Expected, written before running: no case differs, because nothing the plugin shows depends on fast-uri. A lone difference on a link case would come from a real registry or status list, and would be rerun before being believed.
    Result: 0 of 21 differ, as expected (live demo at d575001 against pr-38 at b41d54a, 29 October 2026). The same comparison of the live demo against itself, run first as a noise check, also gave 0 of 21. Details, and a check by hand, in the smoke-test comment below.

Reaching the wallet

Nothing changes in the wallet until its verifier-plugin pin is bumped. Its lockfile pins release commit a4ca3e6 (15 October), an older build than the current release branch, and that build also bundles fast-uri 3.1.8 (checked 29 October 2026).

Separately, the wallet has its own fast-uri 3.1.7, through its own ajv (used by its verifier-core 1.x and by vanilla-jsoneditor). Neither this PR nor a pin bump changes that copy; it needs the wallet's own lockfile.

🤖 Generated with Claude Code

fast-uri is bundled into dist through verifier-core's ajv. Of the two
packages in #37 that reach the built plugin, it is the only one with a
fix (@stablelib/ed25519 waits on verifier-core#84). The lockfile moves
from 3.1.8 to 3.1.9 (published 19 October 2026 with npm provenance);
nothing else changes, and package.json is untouched.

The built plugin differs from main's only in fast-uri: dist/index.js is
byte-identical outside fast-uri's three bundled modules, and
THIRD_PARTY_LICENSES.md changes only its version line.

Checked: lint, typecheck, 105,720 tests, 60 browser tests (1 skipped, as
on any local run).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Try it: https://digitalcredentials.github.io/verifier-plugin/pr-38/

Updates on every push to this PR, and goes away when it closes. The browser tests run against this copy too.

@threeqube

Copy link
Copy Markdown
Collaborator Author

Smoke test

The live demo (main at d575001, fast-uri 3.1.8) against this PR's preview (pr-38 at b41d54a, which is main plus this PR, fast-uri 3.1.9). The versions were read from each site's THIRD_PARTY_LICENSES.md.

Expected, written before running: no case differs.

Automated, with a local compare script at phone width, comparing every line the verifier-plugin box shows:

  • Noise check first, the live demo against itself: 0 of 21 differ.
  • Live demo against pr-38: 0 of 21 differ (12 demo buttons, 9 vc-test-fixtures links).

By hand, both sites side by side:

Case Headline How it was built Log
Verified Verified as the standard expects verified
Built wrong This credential is missing information it should have missing details the standard requires malformed
Changed This credential has been tampered with not checked invalid_signature
Withdrawn The issuer has deactivated this copy as the standard expects withdrawn

Each case is identical on both sites. "How it was built" is the only row that depends on fast-uri (through ajv, in the schema check). It passes, flags a problem, and is skipped behind a broken seal, exactly as before.

CI also passed on Node 24, including the browser tests run against the published preview.

@threeqube

Copy link
Copy Markdown
Collaborator Author
Screenshot 2026-10-29 at 2 22 20 PM Screenshot 2026-10-29 at 2 22 27 PM Screenshot 2026-10-29 at 2 22 50 PM Screenshot 2026-10-29 at 2 22 57 PM Screenshot 2026-10-29 at 2 23 12 PM Screenshot 2026-10-29 at 2 23 29 PM Screenshot 2026-10-29 at 2 23 54 PM Screenshot 2026-10-29 at 2 23 58 PM

@threeqube
threeqube merged commit 713b8b4 into main Oct 29, 2026
6 checks passed
@threeqube
threeqube deleted the fast-uri-3.1.9 branch October 29, 2026 21:27
github-actions Bot added a commit that referenced this pull request Oct 29, 2026
@threeqube

Copy link
Copy Markdown
Collaborator Author

Post-merge check (29 October 2026)

Checked on the live demo and the release branch at main 713b8b4, this PR's merge. The expectations were written before checking.

  • Landed: the live demo serves 713b8b4. It and the release branch both bundle fast-uri 3.1.9.
  • Release: the release branch's dist/ is byte-identical to a fresh build of 713b8b4.
  • Alerts: Move to verifier-core main, and say what its new results mean #22–25 (fast-uri) closed as fixed when this merged.
  • Browser tests against the live demo: 58 passed, 3 skipped (the cross-site tests, which need a local second site). CI's own run against the live demo after the merge also passed.
  • Compare tool:
    • The live demo against itself: 0 of 21 cases differ.
    • The live demo's screenshots against those of the pr-38 preview, taken before merge: 19 of 21 are byte-identical. The other two (Not yet valid, Changed) differ by 8 and 16 pixels, each by one colour level out of 255, along the box's border. That is the browser smoothing edges, not a change: the same difference appears between two screenshots taken before merge, which the tool also called the same.

The rescan at merge raised 7 new undici alerts. Their advisories were published on 27 October, so this PR didn't cause them. They are dev-only, and PR 2 in #37 clears them with the same undici versions it already planned. #37 is updated.

The wallet still pins an earlier build (a4ca3e6), so this reaches it only when that pin is bumped.

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.

1 participant