Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: fastify/fast-uri
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v3.1.8
Choose a base ref
...
head repository: fastify/fast-uri
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v3.1.9
Choose a head ref
  • 12 commits
  • 9 files changed
  • 4 contributors

Commits on Oct 18, 2026

  1. Configuration menu
    Copy the full SHA
    8e8e5c6 View commit details
    Browse the repository at this point in the history

Commits on Oct 19, 2026

  1. fix(SEC-01): reject percent-encoded URI schemes instead of decoding them

    RFC 3986 section 3.1 defines scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
    and does not allow percent-encoding. The parser and serializer decoded the
    scheme with the deprecated `unescape()` global, which also accepts the
    JavaScript-only `%uXXXX` form. As a result an inert, invalid input could be
    turned into a live scheme by fast-uri itself:
    
      normalize('%6Aavascript:alert(1)')       // 'javascript:alert(1)'
      normalize('%u006Aavascript:alert(1)')    // 'javascript:alert(1)'
      resolve('http://a/b', '%6Aavascript:x')  // 'javascript:x'
      equal('%68ttp://a/', 'http://a/')        // true
    
    The scheme is now validated as-is. A scheme containing "%" is reported as
    malformed, so normalize() returns the input unchanged, equal() returns false
    and resolve()/serialize() throw. `unescape()` is no longer used.
    
    BREAKING CHANGE: percent-encoded schemes (e.g. `ht%74ps:`) are no longer
    decoded and are treated as malformed.
    
    (cherry picked from commit f21e3c60c35c5dc4428c6078243e9877230f8210)
    zekth authored and mcollina committed Oct 19, 2026
    Configuration menu
    Copy the full SHA
    597f254 View commit details
    Browse the repository at this point in the history
  2. fix(SEC-02): preserve meaning of relative references during normaliza…

    …tion
    
    normalize()/serialize() applied RFC 3986 remove_dot_segments to relative-path
    references, which is only defined for merged target paths. This turned
    document-relative references into different targets: 'a/../b' became '/b'
    (root-relative), 'x/..' became '/', and '../x' became 'x', so equal('../x', 'x')
    was true.
    
    Add removeDotSegmentsRelative() in lib/utils.js and use it in serialize() when
    there is no scheme, no authority and the path does not start with '/'. It
    collapses '.' and 'seg/..' pairs, keeps leading '..' segments that cannot be
    collapsed, keeps a trailing-directory meaning ('a/b/..' -> 'a/'), and prefixes
    '.' when the result would be empty or start with an empty segment ('x/..' ->
    './', 'a/..//x' -> './/x'), so it never becomes root-relative, never introduces
    an authority and never collapses to the same-document reference ''.
    
    resolve() still accepts relative base URIs: Ajv, the main consumer, resolves
    schema ids against relative bases ('' / '#' / relative $id), and rejecting them
    breaks test/ajv.test.js.
    
    Adds test/relative-normalization-security.test.js with a seeded property test
    asserting resolve(b, normalize(r)) === resolve(b, r) for absolute bases and
    normalize() idempotence.
    
    (cherry picked from commit 0efc4dcf921a5a36ddefd8496089d3ed77ff8e47)
    zekth authored and mcollina committed Oct 19, 2026
    Configuration menu
    Copy the full SHA
    b110a35 View commit details
    Browse the repository at this point in the history
  3. Configuration menu
    Copy the full SHA
    eb483b5 View commit details
    Browse the repository at this point in the history
  4. Configuration menu
    Copy the full SHA
    d86a8d2 View commit details
    Browse the repository at this point in the history
  5. fix(SEC-03): reject non reg-name characters in domain hosts

    For domain-host schemes, `canonicalizeHost()` passed the host straight to
    `new URL('http://' + host)` and used the returned hostname, without checking
    that the WHATWG parser had read the whole input as a host. Because the
    RFC 3986 parse lets "@" into the host group, and WHATWG strips TAB/LF/CR, the
    reported authority was silently rewritten by a second parser, with no error:
    
      parse('http://a@[email protected]/').host   // 'evil.com'  ("b" dropped)
      normalize('http://ev\til.com/p')     // 'http://evil.com/p'
      equal('http://ev\til.com/', 'http://evil.com/') // true
      resolve('http://x/', '//a@[email protected]/p')        // 'http://[email protected]/p'
    
    Domain hosts containing an ASCII character outside the RFC 3986 reg-name set
    (unreserved, sub-delims, pct-encoded) now fail closed with
    'URI host is malformed.', regardless of `unicodeSupport`. `resolve()` checks
    the final host once the effective scheme is known and throws, including for
    percent-encoded hosts that previously bypassed the error. As a further
    safeguard, the WHATWG result is accepted only when it contains nothing but a
    hostname (no userinfo, port, path, query or fragment).
    
    Schemes without domain hosts keep their existing RFC 3986 encoding behaviour.
    
    (cherry picked from commit 07dfb95f02db31e8c241a13c10b6b5d51b27fa16)
    zekth authored and mcollina committed Oct 19, 2026
    Configuration menu
    Copy the full SHA
    ddfc6a5 View commit details
    Browse the repository at this point in the history
  6. Configuration menu
    Copy the full SHA
    745183c View commit details
    Browse the repository at this point in the history
  7. fix: escape a first-segment colon in suffix references

    A suffix reference emits no scheme token, so its first path segment must not contain a bare colon or the output reparses as an absolute URI.
    
    Ref: GHSA-333x-w4r6-6crp
    UlisesGascon authored and mcollina committed Oct 19, 2026
    Configuration menu
    Copy the full SHA
    39e6493 View commit details
    Browse the repository at this point in the history
  8. Configuration menu
    Copy the full SHA
    54c30f7 View commit details
    Browse the repository at this point in the history
  9. Configuration menu
    Copy the full SHA
    9eb8759 View commit details
    Browse the repository at this point in the history
  10. Configuration menu
    Copy the full SHA
    a1d0da2 View commit details
    Browse the repository at this point in the history
  11. Bumped v3.1.9

    github-actions[bot] committed Oct 19, 2026
    Configuration menu
    Copy the full SHA
    0def703 View commit details
    Browse the repository at this point in the history
Loading