Back to Blog
high SEVERITY6 min read

How Security Policy Bypass via Improper Unicode Hostname Canonicalization Happens in Node.js and How to Fix It

A high-severity vulnerability (CVE-2026-13676) in the `fast-uri` npm package allowed attackers to bypass security policies through improper Unicode hostname canonicalization. The fix upgrades `fast-uri` from version 3.1.0 to 4.1.2 using npm overrides to ensure the patched version is used throughout the entire dependency tree of the `ide-agent-kit` project.

O
By Orbis AppSec
Published August 26, 2026Reviewed August 26, 2026

Answer Summary

CVE-2026-13676 is a high-severity security policy bypass vulnerability in the `fast-uri` npm package caused by improper Unicode hostname canonicalization. Attackers can craft URIs with visually similar Unicode characters that bypass hostname allowlists and blocklists. The fix is to upgrade `fast-uri` to version 4.0.1+ (or 3.1.3+, 2.4.2+) and use npm overrides to enforce the patched version across all transitive dependencies.

Vulnerability at a Glance

cweCWE-436 (Interpretation Conflict) / CWE-173 (Improper Handling of Alternate Encoding)
fixUpgrade fast-uri to 4.1.2 and enforce it via npm overrides across the dependency tree
riskAttackers can bypass URL-based security policies (allowlists, blocklists, SSRF protections)
languageJavaScript/Node.js
root causefast-uri failed to properly canonicalize Unicode characters in hostnames before comparison
vulnerabilitySecurity Policy Bypass via Improper Unicode Hostname Canonicalization

Introduction

In the ide-agent-kit repository, a high-severity vulnerability was discovered lurking in the dependency tree — specifically in fast-uri version 3.1.0, a widely-used URI parsing library in the Node.js ecosystem. The vulnerability, tracked as CVE-2026-13676, allows attackers to bypass security policies that rely on hostname comparison by exploiting improper Unicode canonicalization in URI parsing.

The fast-uri package is commonly pulled in as a transitive dependency through schema validation libraries like Ajv, which means many Node.js applications are exposed without developers even realizing they depend on it. In this case, the package-lock.json pinned fast-uri at version 3.1.0, which contained the flawed hostname parsing logic.

This matters for any developer building applications that make security decisions based on parsed URIs — think SSRF protections, OAuth redirect validation, webhook URL allowlists, or any feature that checks "is this hostname trusted?"

The Vulnerability Explained

What Is Unicode Hostname Canonicalization?

When a URI contains a hostname, that hostname can be represented in multiple ways using Unicode. For example, the Cyrillic letter "а" (U+0430) looks visually identical to the Latin "a" (U+0061), but they are different code points. Similarly, characters like "ℊ" (U+210A) can be confused with "g", and full-width characters like "e" (U+FF45) look like "e".

Proper URI parsing requires canonicalization — converting all these representations to a single, consistent form (typically ASCII via Punycode for internationalized domain names) before making any security decisions.

The Flaw in fast-uri 3.1.0

The vulnerable version of fast-uri (3.1.0) failed to properly canonicalize Unicode characters in hostnames. When parsing a URI like:

https://ехаmрlе.com/api/data

(where several Latin characters are replaced with visually identical Cyrillic homoglyphs), fast-uri would return the hostname as-is without converting it to its Punycode equivalent (xn--...). This means that security code comparing the parsed hostname against an allowlist of trusted domains would see a mismatch — or worse, an attacker could craft a hostname that matches a trusted domain when it shouldn't.

Attack Scenario

Consider a scenario where the ide-agent-kit application validates webhook URLs or API endpoints against a blocklist:

const { parse } = require('fast-uri');

function isBlockedHost(url) {
  const parsed = parse(url);
  const blocked = ['internal-api.company.com', 'metadata.google.internal'];
  return blocked.includes(parsed.host);
}

// Attacker submits a URL with Unicode homoglyphs
const maliciousUrl = 'https://іnternal-apі.company.com/secrets';
// The Cyrillic "і" (U+0456) bypasses the blocklist check
console.log(isBlockedHost(maliciousUrl)); // false — bypass!

The attacker's URL resolves to a different DNS entry they control, or in some network configurations, the Unicode hostname bypasses the policy check entirely while still routing to the intended internal service.

Real-World Impact

For the ide-agent-kit project (an IDE agent toolkit), this vulnerability could allow:

  1. SSRF bypass: If the agent makes HTTP requests to user-specified URLs with hostname validation, an attacker could reach internal services.
  2. OAuth/redirect bypass: If the toolkit validates redirect URIs, homoglyph hostnames could redirect to attacker-controlled domains.
  3. Policy circumvention: Any security policy built on hostname comparison becomes unreliable.

The Fix

The fix involves two coordinated changes across package.json and package-lock.json:

1. Adding an npm Override in package.json

Before:

{
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.29.0",
    "user-intent-kit": "file:packages/user-intent-kit"
  }
}

After:

{
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.29.0",
    "user-intent-kit": "file:packages/user-intent-kit"
  },
  "overrides": {
    "fast-uri": "4.1.2"
  }
}

The overrides field in package.json is critical here. Since fast-uri is a transitive dependency (pulled in by other packages like Ajv), simply upgrading direct dependencies might not be enough. The override forces npm to resolve fast-uri to version 4.1.2 everywhere in the dependency tree, regardless of what version ranges other packages request.

2. Updating package-lock.json

Before:

"node_modules/fast-uri": {
  "version": "3.1.0",
  "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
  "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="
}

After:

"node_modules/fast-uri": {
  "version": "4.1.2",
  "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-4.1.2.tgz",
  "integrity": "sha512-TyGmBcbDTZXcb2cj5MV89DrF42DKvb3y5DDUNh95iO+IMeAzMkVSxK1PZRrRIpc9yg8U2GhGdbofNa0LS/a4Bw=="
}

Why This Fixes the Problem

Version 4.1.2 of fast-uri properly canonicalizes Unicode hostnames before returning them from the parse function. This means:

  • Homoglyph characters are normalized to their canonical representations
  • Internationalized domain names are properly converted to Punycode
  • Security policies comparing hostnames get consistent, canonical values regardless of how the input was encoded

The project version was also bumped from 0.9.0 to 0.10.1, signaling to consumers that a security-relevant change was made.

Prevention & Best Practices

1. Use npm Overrides for Transitive Dependencies

When a vulnerability exists in a transitive dependency, waiting for intermediate packages to update can leave you exposed for weeks or months. Use overrides (npm) or resolutions (yarn) to force patched versions:

{
  "overrides": {
    "vulnerable-package": ">=patched-version"
  }
}

2. Implement Defense-in-Depth for URL Validation

Never rely on a single layer of hostname validation:

// Layer 1: Parse and canonicalize
const parsed = new URL(userInput); // Use built-in URL API as backup

// Layer 2: Resolve DNS and check IP
const resolved = await dns.resolve(parsed.hostname);
if (isPrivateIP(resolved)) throw new Error('Blocked');

// Layer 3: Network-level controls
// Use egress firewalls to prevent access to internal networks

3. Audit Your Dependency Tree Regularly

Run npm audit regularly and integrate vulnerability scanning (Trivy, Snyk, etc.) into your CI/CD pipeline. Pay special attention to URI/URL parsing libraries since they're security-critical.

4. Prefer ASCII Hostname Comparison

When implementing security policies, convert hostnames to Punycode before comparison:

const punycode = require('punycode/');
const canonicalHost = punycode.toASCII(parsed.hostname);

Key Takeaways

  • Transitive dependencies like fast-uri can introduce critical vulnerabilities that don't appear in your direct dependency list — always scan the full dependency tree.
  • Unicode homoglyph attacks on hostnames are a real threat to any application that makes security decisions based on URL parsing — the Cyrillic "а" and Latin "a" are different code points but visually identical.
  • npm overrides are essential for patching transitive dependencies immediately rather than waiting for the entire dependency chain to update.
  • fast-uri 3.1.0's hostname parsing returned un-canonicalized Unicode, making every downstream security check that relied on it potentially bypassable.
  • Version 4.1.2 adds proper Unicode normalization to the hostname parsing path, ensuring consistent canonical output regardless of input encoding.

How Orbis AppSec Detected This

  • Source: External input flowing into URI parsing functions via the fast-uri library (any user-supplied URL processed by components using Ajv schema validation or direct fast-uri calls)
  • Sink: Hostname comparison logic downstream of fast-uri's parse() function, where un-canonicalized Unicode hostnames are compared against security policy lists
  • Missing control: Unicode-to-ASCII (Punycode) canonicalization of hostnames before security-relevant comparison operations
  • CWE: CWE-436 (Interpretation Conflict) — the URI parser and security policy logic interpret the same hostname differently due to missing canonicalization
  • Fix: Upgraded fast-uri from 3.1.0 to 4.1.2 via npm overrides, ensuring proper Unicode hostname canonicalization across the entire dependency tree

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

CVE-2026-13676 is a reminder that security vulnerabilities don't always live in your own code — they can hide deep in your dependency tree in packages you've never directly imported. The fast-uri library's failure to canonicalize Unicode hostnames created a gap that could undermine any security policy built on hostname comparison.

The fix was straightforward: upgrade to a patched version and use npm overrides to ensure consistency across the dependency tree. But the lesson is broader — whenever your application makes trust decisions based on parsed data, ensure that the parser produces canonical, normalized output. Unicode is powerful and complex, and its complexity is a fertile ground for security bypasses.

Stay vigilant, keep your dependencies updated, and implement defense-in-depth for any security-critical parsing logic.

References

Frequently Asked Questions

What is improper Unicode hostname canonicalization?

It occurs when a URI parser fails to normalize Unicode characters (like homoglyphs or encoded sequences) in hostnames to their canonical form, allowing visually similar but technically different hostnames to bypass security checks.

How do you prevent Unicode hostname bypass in Node.js?

Use URI parsing libraries that properly canonicalize hostnames (converting to Punycode/ASCII), keep dependencies updated, and implement defense-in-depth with multiple validation layers rather than relying solely on string comparison.

What CWE is improper Unicode hostname canonicalization?

It maps to CWE-436 (Interpretation Conflict) and CWE-173 (Improper Handling of Alternate Encoding), where different components interpret the same data differently due to encoding inconsistencies.

Is URL validation alone enough to prevent hostname bypass?

No. URL validation must include proper canonicalization of Unicode hostnames to ASCII (Punycode) before comparison. Without canonicalization, visually identical hostnames using different Unicode representations can bypass validation.

Can static analysis detect Unicode hostname bypass?

Yes. Tools like Trivy can detect known vulnerable versions of libraries like fast-uri through CVE databases. However, detecting the underlying logic flaw in custom code typically requires specialized Unicode-aware analysis or manual review.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #68

Related Articles

high

How ReDoS happens in Node.js path-to-regexp and how to fix it

CVE-2024-52798 is a Regular Expression Denial of Service (ReDoS) vulnerability in the `path-to-regexp` package's 0.1.x branch, which remains unpatched in that legacy line. Because `path-to-regexp` is a transitive dependency pulled in by `websocket-driver` and many other popular Node.js packages, any application that processes attacker-controlled URL paths through an affected version is at risk of catastrophic backtracking that can freeze the event loop. Upgrading `websocket-driver` to 0.7.5 — an

high

How Denial of Service via Crafted Long-Path Tar Archives Happens in Node.js and How to Fix It

CVE-2026-73566 is a Denial of Service vulnerability in node-tar that allows attackers to craft specially malformed tar archives with excessively long file paths to exhaust system resources and crash applications. The fix upgrades tar from version 7.5.19 to 7.5.21, which implements proper path length validation to prevent this attack vector.

high

How Denial of Service via Memory Exhaustion happens in Socket.IO Parser and how to fix it

CVE-2026-69185 is a high-severity Denial of Service vulnerability in the `socket.io-parser` package that allows attackers to exhaust server memory by sending specially crafted packets. The fix upgrades `socket.io-parser` from version 4.2.4 to 4.2.7 (and parallel branches to 3.4.5 and 3.3.6) in `client/package-lock.json`, closing the attack surface against malicious clients. This kind of memory-exhaustion flaw is particularly dangerous in real-time applications where the parser handles a continuo

high

How express-check-csurf-middleware-usage happens in JavaScript/Express and how to fix it

A high-severity CSRF vulnerability was identified in `tower_game/index.js` where the Express application lacked any Cross-Site Request Forgery protection middleware. Without CSRF validation, an attacker could craft malicious pages that trick authenticated users into submitting unwanted requests to the game server. The fix adds `csurf` middleware with cookie-based token storage in just four lines of code.

high

How Quadratic CPU Consumption Happens in js-yaml and How to Fix It

A high-severity denial-of-service vulnerability in js-yaml versions prior to 4.3.1 allowed attackers to craft malicious YAML documents with !!omap tags that triggered quadratic CPU consumption during parsing. This fix upgrades js-yaml from 4.1.1 to 4.3.1 using npm overrides, protecting applications from algorithmic complexity attacks that could freeze or crash Node.js services.

high

How trailofbits.python.pickles-in-pytorch.pickles-in-pytorch happens in Python/PyTorch and how to fix it

A high-severity deserialization vulnerability was fixed in `skills/packs/pipeline-phase-5-pretrain-code/scripts/trainer.py` where `torch.save()` was used to serialize model checkpoints. Because PyTorch's save mechanism relies on Python's `pickle` module internally, any checkpoint file loaded later could execute arbitrary code. The fix replaces `torch.save()` with `np.savez()` for model weights and a JSON file for metadata, eliminating the pickle-based serialization entirely.