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:
- SSRF bypass: If the agent makes HTTP requests to user-specified URLs with hostname validation, an attacker could reach internal services.
- OAuth/redirect bypass: If the toolkit validates redirect URIs, homoglyph hostnames could redirect to attacker-controlled domains.
- 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-urican 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
overridesare essential for patching transitive dependencies immediately rather than waiting for the entire dependency chain to update. fast-uri3.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-urilibrary (any user-supplied URL processed by components using Ajv schema validation or directfast-uricalls) - Sink: Hostname comparison logic downstream of
fast-uri'sparse()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-urifrom 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
- CWE-436: Interpretation Conflict
- CWE-173: Improper Handling of Alternate Encoding
- OWASP Server-Side Request Forgery Prevention Cheat Sheet
- OWASP Input Validation Cheat Sheet
- npm overrides documentation
- fast-uri npm package
- Semgrep rules for SSRF
- fix: upgrade fast-uri to 4.0.1, 3.1.3, 2.4.2 (CVE-2026-13676)