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
•Technically reviewed by Anupam Mediratta•Published August 26, 2026•Reviewed 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.

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.

Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #68

Related Articles

high

MapManager.get() Race Condition Duplicates API Requests

The MapManager's `get(mapUid, cache)` method used a check-then-act pattern that permitted multiple concurrent requests to pass the cache miss check simultaneously, triggering redundant API calls and risking cache corruption. The fix introduces a `_pending` promise map to deduplicate in-flight fetches for identical map UIDs.

critical

Lampa Desktop Auto-Update Heuristic Bypass: Execution of Unverified

Lampa Desktop's auto-update mechanism downloaded JavaScript and CSS from `raw.githubusercontent.com` using only heuristic validation—file size thresholds and string pattern matching—that attackers could trivially satisfy. The fix introduces cryptographic integrity verification by cross-referencing Git blob hashes from the GitHub Contents API, ensuring downloaded code matches the repository's authoritative state before execution.

high

adm-zip 0.6.0 Preserves SUID Bits From ZIPs: CVE-2026-102282

The `adm-zip` dependency resolved to 0.6.0 in this project's dependency tree, a version affected by CVE-2026-102282: during extraction it applies the Unix permission bits stored in each ZIP entry's external file attributes verbatim, including the setuid (`04000`), setgid (`02000`), and sticky bits. An attacker who controls an archive passed to `extractAllTo()` or `extractEntryTo()` can therefore have the extractor create a setuid binary owned by whatever user the extraction process runs as. The

high

requestInput() Type Confusion: NaN and Object Bypass in JavaScript

The `requestInput()` utility function lacked validation on its `type` parameter and failed to handle `NaN` results from float conversions, creating a type confusion weakness. An attacker could supply malformed inputs that propagate unhandled `NaN` values or unexpected object types through the type system. The fix adds explicit guards against `NaN` type parameters and rejects non-primitive type values.

critical

No Rate Limit on /api/uploads/presign Enables DoS

The `/api/uploads/presign` endpoint accepted unlimited concurrent requests to generate storage presigned URLs, giving an attacker a free lever to exhaust storage-provider quotas and server resources. The fix adds an `express-rate-limit` middleware capping each client to 30 requests per minute on that route.

high

CVE-2026-54673: builder-util-runtime Leaks Auth Headers on Redirect

electron-updater and electron-builder rely on builder-util-runtime to fetch update manifests and artifacts over HTTP. A flaw in that shared HTTP executor allowed credential headers attached to the original update-feed request to be re-sent after a redirect, exposing them to any host the redirect pointed to. The project fixes this by upgrading builder-util-runtime to 9.7.0 and collapsing a duplicate, older copy of the package that electron-updater had pinned on its own.