CVE-2026-63460: Vendure Unauthenticated ReDoS via SQLite Regex Filter
An unauthenticated attacker can send a single GraphQL request with a catastrophically backtracking regex to a Vendure shop running on SQLite, freezing the Node.js event loop and taking the entire…
The problem
Vendure registers a JavaScript user-defined function so SQLite can evaluate the REGEXP operator used by StringOperators.regex list filters. That UDF runs synchronously on the Node.js event loop via better-sqlite3 or sqljs.
The products query on the Shop GraphQL API carries no @Allow decorator, so it is publicly accessible with no credentials. Supplying a nested-quantifier pattern like (a+)+$ against a long string causes catastrophic backtracking, pegging the CPU for tens of seconds and making the storefront and admin panel completely unresponsive.
Repeated requests sustain the denial of service with minimal bandwidth.
Proof of concept
A working proof-of-concept for CVE-2026-63460 in vendure/core, with the exact payload below.
curl -s -X POST http://localhost:3000/shop-api \
-H "Content-Type: application/json" \
-d '{"query":"{ products(options:{filter:{name:{regex:\"(a+)+$\"}}}) { items { id } } }"}'The root cause (CWE-1333) is that list-query-builder.ts passes the raw StringOperators.regex value directly into new RegExp(pattern, 'i').test(value) inside the SQLite UDF, with no length cap, no safe-regex check, and no timeout. The pattern (a+)+$ is a classic nested-quantifier trap: matching it against 'a'.repeat(28) + 'b' takes roughly 20 seconds on a modern CPU, verified on Node.js v24.
The patch (commit f74cbbb) hardens input handling in the filter path. It either validates the pattern against a safe-regex library before constructing the RegExp, enforces a maximum pattern length, or both, so malicious patterns are rejected before they ever reach the UDF.
The fix
Upgrade @vendure/core to **3.6.5** (or 3.7.0+). Only deployments using the better-sqlite3 or sqljs database driver are affected; PostgreSQL and MySQL/MariaDB are not vulnerable because they delegate regex evaluation to the database engine, not the Node.js event loop.
As a defence-in-depth measure, consider adding @Allow(Permission.Authenticated) to ShopProductsResolver.products if anonymous product browsing is not required.
Related research
- highCVE-2026-86081CVE-2026-86081: n8n Regular Expression Denial of Service via Git Node Clone Path
- high@tiptap/core Quadratic ReDoS in Markdown Attribute Parsing
- highCVE-2026-83619CVE-2026-83619: @xmldom/xmldom End-Tag Whitespace ReDoS
- highCVE-2026-83606CVE-2026-83606: @xmldom/xmldom Processing Instruction ReDoS