VYPR
Medium severity5.3NVD Advisory· Published Jun 15, 2026· Updated Jun 15, 2026

UAParser.js: Unbounded `Sec-CH-UA-Model` parsing can trigger ReDoS in `withClientHints()`

CVE-2026-48125

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

Patches

1
90354d345849

Fix: Prevent ReDoS vulnerability by limiting Client Hints input length (GHSA-9h5v-pfqq-x599)

https://github.com/faisalman/ua-parser-jsFaisal SalmanMay 21, 2026Fixed in 2.0.10via ghsa-release-walk
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

2

News mentions

0

No linked articles in our index yet.