CVE-2026-106104
Quasar Framework: Super-linear regex backtracking on User-Agent lets one request stall a Quasar SSR server
Quasar Framework is a framework for building high-performance Vue.js user interfaces. Prior to 2.23.3, Platform.parseSSR() passed an unbounded User-Agent request header to getMatch() in ui/src/plugins/platform/Platform.js, whose browser-detection expressions combined greedy captures with repeated unbounded scans. Platform belongs to autoInstalledPlugins, so this parsing occurs before routing for every SSR request. A crafted unauthenticated request containing repeated version tokens without a terminating Safari token causes super-linear backtracking and blocks the Node.js event loop, delaying every other SSR request. SPA, PWA, Electron, Cordova, Capacitor, browser-extension, and static-site-generation targets are not affected because they do not parse an attacker-controlled request header through this path. This issue is fixed in version 2.23.3.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- None
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
8.7
Quasar Framework: Super-linear regex backtracking on User-Agent lets one request stall a Quasar SSR server
Quasar Framework is a framework for building high-performance Vue.js user interfaces. Prior to 2.23.3, Platform.parseSSR() passed an unbounded User-Agent request header to getMatch() in ui/src/plugins/platform/Platform.js, whose browser-detection expressions combined greedy captures with repeated unbounded scans. Platform belongs to autoInstalledPlugins, so this parsing occurs before routing for every SSR request. A crafted unauthenticated request containing repeated version tokens without a terminating Safari token causes super-linear backtracking and blocks the Node.js event loop, delaying every other SSR request. SPA, PWA, Electron, Cordova, Capacitor, browser-extension, and static-site-generation targets are not affected because they do not parse an attacker-controlled request header through this path. This issue is fixed in version 2.23.3.
- Record
CVE-2026-106104- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N - Weakness
- CWE-1333
- Updated
- 2026-11-02
EUVDEUVD-2026-93569
8.7
Quasar Framework is a framework for building high-performance Vue.js user interfaces. Prior to 2.23.3, Platform.parseSSR() passed an unbounded User-Agent request header to getMatch() in ui/src/plugins/platform/Platform.js, whose browser-detection expressions combined greedy captures with repeated unbounded scans. Platform belongs to autoInstalledPlugins, so this parsing occurs before routing for every SSR request. A crafted unauthenticated request containing repeated version tokens without a terminating Safari token causes super-linear backtracking and blocks the Node.js event loop, delaying every other SSR request. SPA, PWA, Electron, Cordova, Capacitor, browser-extension, and static-site-generation targets are not affected because they do not parse an attacker-controlled request header through this path. This issue is fixed in version 2.23.3.
- Record
EUVD-2026-93569- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-68jq-fhch-4xq4
8.7
Quasar Framework: Super-linear regex backtracking on User-Agent lets one request stall a Quasar SSR server
Summary
One unauthenticated request with a crafted User-Agent stalls a Quasar SSR server for seconds.
Quasar auto-installs its Platform plugin on every server-side render, and Platform.parseSSR() feeds the raw, unbounded User-Agent request header into a chain of backtracking regular expressions in getMatch(). One of those patterns contains a greedy capture followed by two unbounded .* scans, so a crafted header costs time proportional to the cube of its length. An 8 KB User-Agent blocks the Node.js event loop for about 4.4 seconds, and a 16 KB one for about 35 seconds. During that time the server answers nobody, so a handful of tiny requests take an SSR site completely offline.
Details
Platform is in the autoInstalledPlugins array in ui/src/install-quasar.js, so it is installed unconditionally by app.use(Quasar, ...). The generated SSR entry (app-vite/templates/entry/app.js, called from app-vite/templates/entry/server-entry.js) runs that for every HTTP request, and it runs before routing, so requests to paths that do not exist are affected too. On the server the plugin takes the header verbatim (ui/src/plugins/platform/Platform.js):
Platform.parseSSR = ssrContext => {
const ua =
ssrContext.req.headers['user-agent'] ||
ssrContext.req.headers['User-Agent'] ||
''
return { ...client, userAgent: ua, is: getPlatform(ua) }
}
getPlatform() lowercases the string and passes it to getMatch() (ui/src/plugins/platform/Platform.js:23-45), which evaluates an ordered chain of exec() calls. The sixth alternative, at ui/src/plugins/platform/Platform.js:32-34, is the problem:
/(webkit)[\/]([\w.]+).*(version)[\/]([\w.]+).*(safari)[\/]([\w.]+)/.exec(userAgent)
([\w.]+) is greedy and unbounded, and it is followed by two unbounded .* scans. When the input contains webkit/, many version/ tokens, and no safari/, the pattern can only fail after the engine has tried every combination of "where ([\w.]+) stops" against "which version occurrence the first .* lands on" against "how far the second .* searches for safari". That is O(k * m * L) states, and none of the earlier alternatives short-circuit it because none of them match. The fifth alternative has the same shape.
Measured on the functions loaded verbatim out of ui/src/plugins/platform/Platform.js, cost grows by a factor of eight for every doubling of the header:
UA 3998 B -> 554 ms
UA 7998 B -> 4368 ms fits nginx default large_client_header_buffers 8k
UA 15998 B -> 34995 ms fits Node.js default --max-http-header-size 16k
A same-length header of ordinary characters costs 0.1 ms, so this is the regex and not the length.
Client-side rendering is not affected: there getPlatform() only ever sees navigator.userAgent, which the attacker does not control. The problem is specific to the SSR path, where the string arrives from the network.
PoC
The vulnerable pattern ships in the published package. From node_modules/quasar/dist/quasar.server.prod.js of [email protected]:
function M(e,t){let n=/(edg|edge|edga|edgios)\/([\w.]+)/.exec(e)||...
||/(webkit)[\/]([\w.]+).*(version)[\/]([\w.]+).*(safari)[\/]([\w.]+)/.exec(e)||...
I.parseSSR=e=>{let t=e.req.headers[`user-agent`]||e.req.headers[`User-Agent`]||``;
return{...F,userAgent:t,is:P(t)}};
Build the header:
const k = 3200 // filler that maximises the greedy capture
const m = 479 // "version/" tokens for the first .* to land on
const ua = 'webkit/' + 'a'.repeat(k) + ' ' + 'version/1 '.repeat(m) // 7998 bytes
Server used for the end to end run, which performs exactly the per-request work Quasar SSR performs, through the real published package:
import http from 'node:http'
import { Platform } from 'quasar' // resolves to dist/quasar.server.prod.js
http.createServer((req, res) => {
const platform =…
- Record
GHSA-68jq-fhch-4xq4- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N - Weakness
- CWE-1333
- Updated
- 2026-11-02
NVD
8.7
Quasar Framework is a framework for building high-performance Vue.js user interfaces. Prior to 2.23.3, Platform.parseSSR() passed an unbounded User-Agent request header to getMatch() in ui/src/plugins/platform/Platform.js, whose browser-detection expressions combined greedy captures with repeated unbounded scans. Platform belongs to autoInstalledPlugins, so this parsing occurs before routing for every SSR request. A crafted unauthenticated request containing repeated version tokens without a terminating Safari token causes super-linear backtracking and blocks the Node.js event loop, delaying every other SSR request. SPA, PWA, Electron, Cordova, Capacitor, browser-extension, and static-site-generation targets are not affected because they do not parse an attacker-controlled request header through this path. This issue is fixed in version 2.23.3.
- Record
CVE-2026-106104- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - Weakness
- CWE-1333
- Updated
- 2026-11-02
OSV
8.7
Quasar Framework: Super-linear regex backtracking on User-Agent lets one request stall a Quasar SSR server
Quasar Framework is a framework for building high-performance Vue.js user interfaces. Prior to 2.23.3, Platform.parseSSR() passed an unbounded User-Agent request header to getMatch() in ui/src/plugins/platform/Platform.js, whose browser-detection expressions combined greedy captures with repeated unbounded scans. Platform belongs to autoInstalledPlugins, so this parsing occurs before routing for every SSR request. A crafted unauthenticated request containing repeated version tokens without a terminating Safari token causes super-linear backtracking and blocks the Node.js event loop, delaying every other SSR request. SPA, PWA, Electron, Cordova, Capacitor, browser-extension, and static-site-generation targets are not affected because they do not parse an attacker-controlled request header through this path. This issue is fixed in version 2.23.3.
- Record
CVE-2026-106104- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N - Weakness
- CWE-1333
- Updated
- 2026-11-02
OSVGHSA-68jq-fhch-4xq4
8.7
Quasar Framework: Super-linear regex backtracking on User-Agent lets one request stall a Quasar SSR server
Summary
One unauthenticated request with a crafted User-Agent stalls a Quasar SSR server for seconds.
Quasar auto-installs its Platform plugin on every server-side render, and Platform.parseSSR() feeds the raw, unbounded User-Agent request header into a chain of backtracking regular expressions in getMatch(). One of those patterns contains a greedy capture followed by two unbounded .* scans, so a crafted header costs time proportional to the cube of its length. An 8 KB User-Agent blocks the Node.js event loop for about 4.4 seconds, and a 16 KB one for about 35 seconds. During that time the server answers nobody, so a handful of tiny requests take an SSR site completely offline.
Details
Platform is in the autoInstalledPlugins array in ui/src/install-quasar.js, so it is installed unconditionally by app.use(Quasar, ...). The generated SSR entry (app-vite/templates/entry/app.js, called from app-vite/templates/entry/server-entry.js) runs that for every HTTP request, and it runs before routing, so requests to paths that do not exist are affected too. On the server the plugin takes the header verbatim (ui/src/plugins/platform/Platform.js):
Platform.parseSSR = ssrContext => {
const ua =
ssrContext.req.headers['user-agent'] ||
ssrContext.req.headers['User-Agent'] ||
''
return { ...client, userAgent: ua, is: getPlatform(ua) }
}
getPlatform() lowercases the string and passes it to getMatch() (ui/src/plugins/platform/Platform.js:23-45), which evaluates an ordered chain of exec() calls. The sixth alternative, at ui/src/plugins/platform/Platform.js:32-34, is the problem:
/(webkit)[\/]([\w.]+).*(version)[\/]([\w.]+).*(safari)[\/]([\w.]+)/.exec(userAgent)
([\w.]+) is greedy and unbounded, and it is followed by two unbounded .* scans. When the input contains webkit/, many version/ tokens, and no safari/, the pattern can only fail after the engine has tried every combination of "where ([\w.]+) stops" against "which version occurrence the first .* lands on" against "how far the second .* searches for safari". That is O(k * m * L) states, and none of the earlier alternatives short-circuit it because none of them match. The fifth alternative has the same shape.
Measured on the functions loaded verbatim out of ui/src/plugins/platform/Platform.js, cost grows by a factor of eight for every doubling of the header:
UA 3998 B -> 554 ms
UA 7998 B -> 4368 ms fits nginx default large_client_header_buffers 8k
UA 15998 B -> 34995 ms fits Node.js default --max-http-header-size 16k
A same-length header of ordinary characters costs 0.1 ms, so this is the regex and not the length.
Client-side rendering is not affected: there getPlatform() only ever sees navigator.userAgent, which the attacker does not control. The problem is specific to the SSR path, where the string arrives from the network.
PoC
The vulnerable pattern ships in the published package. From node_modules/quasar/dist/quasar.server.prod.js of [email protected]:
function M(e,t){let n=/(edg|edge|edga|edgios)\/([\w.]+)/.exec(e)||...
||/(webkit)[\/]([\w.]+).*(version)[\/]([\w.]+).*(safari)[\/]([\w.]+)/.exec(e)||...
I.parseSSR=e=>{let t=e.req.headers[`user-agent`]||e.req.headers[`User-Agent`]||``;
return{...F,userAgent:t,is:P(t)}};
Build the header:
const k = 3200 // filler that maximises the greedy capture
const m = 479 // "version/" tokens for the first .* to land on
const ua = 'webkit/' + 'a'.repeat(k) + ' ' + 'version/1 '.repeat(m) // 7998 bytes
Server used for the end to end run, which performs exactly the per-request work Quasar SSR performs, through the real published package:
import http from 'node:http'
import { Platform } from 'quasar' // resolves to dist/quasar.server.prod.js
http.createServer((req, res) => {
const platform =…
- Record
GHSA-68jq-fhch-4xq4- CVSS 4.0
- 8.7 High
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N - Weakness
- CWE-1333
- Updated
- 2026-11-02
Affected products
Affected packages
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-106104 is a high vulnerability (CVSS 8.7) in Quasarframework Quasar, published October 6, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.37%, the 31st percentile. Fixed in quasar 2.23.3.
References
github.com/quasarframework/quasar/security/advisories/GHSA-68jq-fhch-4xq4
github.com/quasarframework/quasar/commit/7a954ddafa756afe95c8f633f48ed68a07209d3d
github.com/quasarframework/quasar/releases/tag/quasar-v2.23.3
nvd.nist.gov/vuln/detail/CVE-2026-106104
github.com/advisories/GHSA-68jq-fhch-4xq4
github.com/CVEProject/cvelistV5/tree/main/cves/2026/106xxx/CVE-2026-106104.json
github.com/quasarframework/quasar
Left out here: the rest of 2 source descriptions. The JSON record has them.