CVE-2026-107378 — cairosvg
Fix: Kozea/CairoSVG@9d63f04CVE-2026-107378 is a CWE-407 vulnerability in cairosvg. A fix is available for cairosvg — see the affected versions and patch details below.
CairoSVG: Quadratic-time DoS parsing a crafted SVG <path>
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
Real-World Exposure
cairosvgReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.
Description
Summary
Rendering an untrusted SVG whose <path d="..."> contains many segments is O(n²) CPU. A single <path> under 1 MiB burns tens of seconds. Two independent O(n²) sites in cairosvg/path.py:
- Tokenizer — the path-data parser consumes the
dstring with awhile string:loop that repeatedly slices/re-scans the remaining string (each step is O(len remaining)), giving O(n²) over the whole attribute. - draw_markers — marker handling drains
node.verticeswithwhile node.vertices: ... node.vertices.pop(0);list.pop(0)is O(n), so draining n vertices is O(n²).
Both are hit on a normal render path (svg2png/svg2pdf), attacker controls only the SVG document.
PoC (installed cairosvg 2.9.0)
import cairosvg
d = "M0 0 " + "L1 1 " * 100000
svg = f'<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10"><path d="{d}"/></svg>'
cairosvg.svg2png(bytestring=svg.encode()) # ~4.4 s for a 488 KB doc
| path segments | SVG size | time |
|---|---|---|
| 50,000 | 244 KB | 1.14 s |
| 100,000 | 488 KB | 4.36 s |
| 200,000 | ~960 KB | ~18 s |
Doubling segments ≈ 4× time ⇒ quadratic. Sub-MiB input ⇒ ~18 s CPU; any service rendering user-supplied SVG (thumbnails, avatars, PDF export) is a DoS target.
Reachability
Public API svg2png / svg2pdf / svg2ps on an untrusted SVG string.
Suggested fix
Tokenize with a single forward scan / index (or re.finditer) instead of re-slicing the remainder; drain vertices with an index or collections.deque.popleft instead of list.pop(0). Optionally cap path-segment count.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐍PyPI | cairosvg | all versions | 2.9.1pip install --upgrade 'cairosvg==2.9.1' |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for cairosvg, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update cairosvg to 2.9.1 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-107378 is resolved across your whole dependency graph.
Workarounds
Cap what an attacker can consume: apply request size, rate and timeout limits in front of the affected component, and run it with memory and CPU limits so exhaustion degrades one worker rather than the whole service.
Frequently Asked Questions
Is CVE-2026-107378 in your dependencies?
Find it across PyPI, including transitive dependencies.