CVE-2026-118664: CVE-2026-118664: Quadratic Complexity Denial of Service in decode-uri-component
CVE-2026-118664: Quadratic Complexity Denial of Service in decode-uri-component Vulnerability ID: CVE-2026-118664 CVSS Score: 8.2 Published: 2026-10-29 An inefficient algorithmic complexity vulnerability exists in t
CVE-2026-118664: Quadratic Complexity Denial of Service in decode-uri-component
Vulnerability ID: CVE-2026-118664
CVSS Score: 8.2
Published: 2026-10-29
An inefficient algorithmic complexity vulnerability exists in the npm package decode-uri-component prior to version 0.5.1. When processing malformed URI strings containing invalid percent-escape sequences, the library falls back to a custom decoder that collects distinct percent-encoded runs into a dictionary and executes a full-string regular expression replacement for each entry. Supplying a payload with thousands of distinct percent-encoded sequences alongside a malformed escape triggers quadratic O(K * L) execution time, starving the single-threaded Node.js event loop and resulting in an unauthenticated Denial of Service.
TL;DR
Versions of decode-uri-component prior to 0.5.1 are vulnerable to an algorithmic complexity Denial of Service (CVE-2026-118664). By sending an unauthenticated payload of approximately 224 KB containing thousands of unique percent-encoded sequences and one malformed escape token, an attacker can force the fallback decoder into quadratic processing time, blocking the Node.js event loop for over 20 seconds per request.
β οΈ Exploit Status: POC
Technical Details
- Vulnerability Type: Inefficient Algorithmic Complexity / Denial of Service
- CWE ID: CWE-407 / CWE-400
- CVSS v4.0 Score: 8.2 HIGH (CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N)
- EPSS Score: 0.32% (24.75th percentile)
- Attack Vector: Network (Unauthenticated HTTP Request)
- Affected Component: decode-uri-component (< 0.5.1)
- Patched Version: 0.5.1
- CISA KEV Status: Not Listed
Affected Systems
- decode-uri-component (npm package)
-
decode-uri-component: < 0.5.1 (Fixed in:
0.5.1)
Code Analysis
Commit: fad01d6
Replace multi-pass replaceMap regex loop with single-pass replacement in customDecodeURIComponent
diff --git a/index.js b/index.js
--- a/index.js
+++ b/index.js
@@ -79,6 +79,15 @@ function decode(input) {
// Continuation byte or invalid lead byte β emit one `%XX` literally.
if (sequenceLength === 0) {
+ const pair = input.slice(position, position + 6);
+
+ // UTF-16 BOM byte pairs become two replacement characters.
+ if (pair === '%FE%FF' || pair === '%FF%FE') {
+ output += '\uFFFD\uFFFD';
+ position += 6;
+ continue;
+ }
+
output += input.slice(position, position + 3);
position += 3;
continue;
@@ -111,7 +120,10 @@ function decode(input) {
}
// Missing continuation bytes or decode failure β emit the first byte literally.
- output += input.slice(position, position + 3);
+ const literal = input.slice(position, position + 3);
+
+ // A truncated `%C2` becomes a replacement character.
+ output += literal === '%C2' ? '\uFFFD' : literal;
position += 3;
}
@@ -120,41 +132,8 @@ function decode(input) {
}
function customDecodeURIComponent(input) {
- // Keep track of all the replacements and prefill the map with the `BOM`
- const replaceMap = {
- '%FE%FF': '\uFFFD\uFFFD',
- '%FF%FE': '\uFFFD\uFFFD',
- };
-
- // Find percent-encoded runs separated by literal text or lone `%` characters.
- let match = multiMatcher.exec(input);
-
- while (match) {
- try {
- // Decode as big chunks as possible
- replaceMap[match[0]] = decodeURIComponent(match[0]);
- } catch {
- const result = decode(match[0]);
-
- if (result !== match[0]) {
- replaceMap[match[0]] = result;
- }
- }
-
- match = multiMatcher.exec(input);
- }
-
- // Add `%C2` at the end of the map to make sure it does not replace the combinator before everything else
- replaceMap['%C2'] = '\uFFFD';
-
- const entries = Object.keys(replaceMap);
-
- for (const key of entries) {
- // Replace all decoded components
- input = input.replace(new RegExp(key, 'g'), replaceMap[key]);
- }
-
- return input;
+ // Decode each percent-encoded run in place in a single pass. Decoded output is never rescanned.
+ return input.replace(multiMatcher, run => decode(run));
}
export default function decodeUriComponent(encodedURI) {
Exploit Details
- GitHub: Unit test in test.js demonstrating a 32,000-run (~224 KB) payload that triggers quadratic execution time on versions prior to 0.5.1.
Mitigation Strategies
- Upgrade decode-uri-component to version 0.5.1 or later across all direct and transitive dependency trees.
- Enforce strict size limits on HTTP query strings, headers, and URL-encoded request bodies at the reverse proxy and application middleware layers.
- Use native decodeURIComponent() and reject malformed URI inputs with HTTP 400 responses if graceful fallback decoding of invalid escape sequences is not required.
- Configure Web Application Firewalls (WAF) to detect and block payloads containing thousands of percent-encoded sequences combined with invalid hexadecimal escapes.
Remediation Steps:
- Run
npm ls decode-uri-componentoryarn why decode-uri-componentto identify all direct and transitive instances of the package in your project. - Update direct dependencies by executing
npm install decode-uri-component@^0.5.1. - If vulnerable versions are pulled transitively via older packages, add an
overridesorresolutionsentry inpackage.jsonforcing"decode-uri-component": "^0.5.1"and regenerate your lockfile. - Verify the remediation by inspecting
package-lock.jsonoryarn.lockto ensure no version prior to0.5.1remains installed.
References
- GitHub Security Advisory GHSA-qr28-fpjm-wq7f
- GitHub Fix Commit fad01d65b93bd3f2f0dc32dfd8d57a38b57bc608
- decode-uri-component v0.5.1 Release
- CVE.org Record: CVE-2026-118664
- NVD Vulnerability Detail: CVE-2026-118664
- cvelistV5 Repository JSON Record for CVE-2026-118664
- CISA Known Exploited Vulnerabilities Catalog
Read the full report for CVE-2026-118664 on our website for more details including interactive diagrams and full exploit analysis.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.