UAParser.js: Unbounded `Sec-CH-UA-Model` parsing can trigger ReDoS in `withClientHints()`
Description
A ReDoS vulnerability in ua-parser-js versions 2.0.1 to 2.0.9 allows unauthenticated attackers to cause denial of service via a crafted Sec-CH-UA-Model header when using withClientHints().
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A ReDoS vulnerability in ua-parser-js versions 2.0.1 to 2.0.9 allows unauthenticated attackers to cause denial of service via a crafted Sec-CH-UA-Model header when using withClientHints().
Vulnerability
A regular expression denial-of-service (ReDoS) vulnerability exists in ua-parser-js versions >=2.0.1 and <=2.0.9 when the withClientHints() API is used. The vulnerable regex / ([\w ]+) miui\/v?\d/i in the device model parser [2][3] causes catastrophic backtracking when processing a long Sec-CH-UA-Model header value. Unlike the User-Agent path, which enforces a hard limit of UA_MAX_LENGTH = 500, the Client Hints path copies input without a length limit before regex evaluation [2][3].
Exploitation
An unauthenticated attacker can send a single HTTP request containing a crafted Sec-CH-UA-Model header (e.g., a 32,000-character string of repeating A ) to any server-side application that calls UAParser(headers).withClientHints() [2][3]. No authentication, user interaction, or special network position is required. The parser then spends excessive CPU time on the regex, with processing time growing polynomially with input length [2][3].
Impact
Successful exploitation causes a denial-of-service (DoS) condition: a single request can consume over 400 ms of CPU time, and repeated requests can exhaust server resources [2][3]. The impact is limited to availability; there is no confidentiality or integrity compromise [2][3].
Mitigation
The maintainers released version 2.0.10 which fixes the vulnerable regular expression and limits Client Hints input length [2][3]. Users should update to 2.0.10 or later. No workaround is available for affected versions [2][3].
AI Insight generated on Jun 15, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2- Range: >=2.0.1, <=2.0.9
Patches
190354d345849Fix: Prevent ReDoS vulnerability by limiting Client Hints input length (GHSA-9h5v-pfqq-x599)
2 files changed · +26 −10
src/main/ua-parser.js+10 −10 modified@@ -170,7 +170,7 @@ itemListToArray = function (header) { if (!header) return undefined; var arr = []; - var tokens = strip(/\\?\"/g, header).split(','); + var tokens = normalizeHeaderValue(header).split(','); for (var i = 0; i < tokens.length; i++) { if (tokens[i].indexOf(';') > -1) { var token = trim(tokens[i]).split(';v='); @@ -187,6 +187,9 @@ majorize = function (version) { return isString(version) ? strip(/[^\d\.]/g, version).split('.')[0] : undefined; }, + normalizeHeaderValue = function (str) { + return isString(str) ? trim(strip(/\\?\"/g, str), UA_MAX_LENGTH) : undefined; + }, setProps = function (arr) { for (var i in arr) { if (!arr.hasOwnProperty(i)) continue; @@ -203,9 +206,6 @@ strip = function (pattern, str) { return isString(str) ? str.replace(pattern, EMPTY) : str; }, - stripQuotes = function (str) { - return strip(/\\?\"/g, str); - }, trim = function (str, len) { str = strip(/^\s\s*/, String(str)); return typeof len === TYPEOF.UNDEFINED ? str : str.substring(0, len); @@ -618,7 +618,7 @@ /oid[^\)]+; (redmi[\-_ ]?(?:note|k)?[\w_ ]+|m?[12]\d[01]\d\w{3,6}|poco[\w ]+|(shark )?\w{3}-[ah]0|qin ?[1-3](s\+|ultra| pro)?)( bui|; wv|\))/i, // Xiaomi Mi /\b(mi[-_ ]?(?:a\d|one|one[_ ]plus|note|max|cc)?[_ ]?(?:\d{0,2}\w?)[_ ]?(?:plus|se|lite|pro)?( 5g|lte)?)(?: bui|\))/i, - / ([\w ]+) miui\/v?\d/i + /; ([\w ]+) miui\/v?\d/i ], [[MODEL, /_/g, ' '], [VENDOR, XIAOMI], [TYPE, MOBILE]], [ // OnePlus @@ -1202,12 +1202,12 @@ [BRANDS, itemListToArray(uach[CH])], [FULLVERLIST, itemListToArray(uach[CH_FULL_VER_LIST])], [MOBILE, /\?1/.test(uach[CH_MOBILE])], - [MODEL, stripQuotes(uach[CH_MODEL])], - [PLATFORM, stripQuotes(uach[CH_PLATFORM])], - [PLATFORMVER, stripQuotes(uach[CH_PLATFORM_VER])], - [ARCHITECTURE, stripQuotes(uach[CH_ARCH])], + [MODEL, normalizeHeaderValue(uach[CH_MODEL])], + [PLATFORM, normalizeHeaderValue(uach[CH_PLATFORM])], + [PLATFORMVER, normalizeHeaderValue(uach[CH_PLATFORM_VER])], + [ARCHITECTURE, normalizeHeaderValue(uach[CH_ARCH])], [FORMFACTORS, itemListToArray(uach[CH_FORM_FACTORS])], - [BITNESS, stripQuotes(uach[CH_BITNESS])] + [BITNESS, normalizeHeaderValue(uach[CH_BITNESS])] ]); } else { for (var prop in uach) {
test/unit/redos.js+16 −0 added@@ -0,0 +1,16 @@ +// GHSA-9h5v-pfqq-x599 + +const assert = require('assert'); +const { UAParser } = require('../../src/main/ua-parser'); + +const headers = { + 'sec-ch-ua-platform': '"Android"', + 'sec-ch-ua-mobile': '?1', + 'sec-ch-ua-model': '"' + 'A '.repeat(25000) + '"' +}; + +const t0 = process.hrtime.bigint(); +UAParser(headers).withClientHints(); +const ms = Number(process.hrtime.bigint() - t0) / 1e6; + +assert(ms < 100); \ No newline at end of file
Vulnerability mechanics
Root cause
"Missing input-length limit on the `Sec-CH-UA-Model` header value combined with a regex that exhibits catastrophic backtracking allows denial-of-service via crafted Client Hints input."
Attack vector
An unauthenticated attacker sends an HTTP request containing a crafted `Sec-CH-UA-Model` header (e.g., a long string of repeating characters like `'A '.repeat(25000)`) to a server-side application that calls `UAParser(headers).withClientHints()`. The vulnerable regex `/ ([\w ]+) miui\/v?\d/i` exhibits catastrophic backtracking on such crafted input, causing CPU time to grow polynomially with input length. A single request with a ~32,000-character model value can consume over 400ms of CPU time, leading to a denial-of-service condition [CWE-1333] [CWE-400] [ref_id=1] [ref_id=2].
Affected code
The vulnerability resides in the device-model regex `/ ([\w ]+) miui\/v?\d/i` at `src/main/ua-parser.js` (line 615 in version 2.0.9). Unlike the `User-Agent` path, which enforces `UA_MAX_LENGTH = 500`, the `withClientHints()` method copies `Sec-CH-UA-Model` values without any length limit before feeding them into regex parsing.
What the fix does
The patch (version 2.0.10) fixes the vulnerable regular expression to eliminate catastrophic backtracking and also introduces a length limit on Client Hints input values before they are passed into regex parsing. This mirrors the existing `UA_MAX_LENGTH = 500` guard that was already applied to `User-Agent` strings but was missing from the `withClientHints()` code path. Together these changes prevent an attacker from supplying an arbitrarily long `Sec-CH-UA-Model` value that would trigger exponential backtracking.
Preconditions
- configThe server-side application must call `UAParser(headers).withClientHints()` on incoming HTTP request headers.
- networkThe attacker must be able to send an HTTP request with a crafted `Sec-CH-UA-Model` header (no authentication required).
- configThe application must be using `ua-parser-js` version >=2.0.1 and <=2.0.9.
Generated on Jun 15, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
2News mentions
0No linked articles in our index yet.