GHSA-C3JG-QH8M-J3H2

Vulnerability from github – Published: 2026-10-08 19:41 – Updated: 2026-10-08 19:41
VLAI
Summary
CairoSVG: Quadratic-time DoS parsing a crafted SVG <path>
Details

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:

  1. Tokenizer — the path-data parser consumes the d string with a while string: loop that repeatedly slices/re-scans the remaining string (each step is O(len remaining)), giving O(n²) over the whole attribute.
  2. draw_markers — marker handling drains node.vertices with while 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.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 2.9.0"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "cairosvg"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.9.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-107378"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-407"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-10-08T19:41:22Z",
    "nvd_published_at": "2026-10-08T18:17:23Z",
    "severity": "HIGH"
  },
  "details": "## Summary\n\nRendering an untrusted SVG whose `\u003cpath d=\"...\"\u003e` contains many segments is O(n\u00b2) CPU. A single `\u003cpath\u003e` under 1 MiB burns tens of seconds. Two independent O(n\u00b2) sites in `cairosvg/path.py`:\n\n1. **Tokenizer** \u2014 the path-data parser consumes the `d` string with a `while string:` loop that repeatedly slices/re-scans the *remaining* string (each step is O(len remaining)), giving O(n\u00b2) over the whole attribute.\n2. **draw_markers** \u2014 marker handling drains `node.vertices` with `while node.vertices: ... node.vertices.pop(0)`; `list.pop(0)` is O(n), so draining n vertices is O(n\u00b2).\n\nBoth are hit on a normal render path (`svg2png`/`svg2pdf`), attacker controls only the SVG document.\n\n## PoC (installed cairosvg 2.9.0)\n\n```python\nimport cairosvg\nd = \"M0 0 \" + \"L1 1 \" * 100000\nsvg = f\u0027\u003csvg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\"\u003e\u003cpath d=\"{d}\"/\u003e\u003c/svg\u003e\u0027\ncairosvg.svg2png(bytestring=svg.encode())   # ~4.4 s for a 488 KB doc\n```\n\n| path segments | SVG size | time |\n|---|---|---|\n| 50,000 | 244 KB | 1.14 s |\n| 100,000 | 488 KB | 4.36 s |\n| 200,000 | ~960 KB | ~18 s |\n\nDoubling segments \u2248 4\u00d7 time \u21d2 quadratic. Sub-MiB input \u21d2 ~18 s CPU; any service rendering user-supplied SVG (thumbnails, avatars, PDF export) is a DoS target.\n\n## Reachability\n\nPublic API `svg2png` / `svg2pdf` / `svg2ps` on an untrusted SVG string.\n\n## Suggested fix\n\nTokenize 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.",
  "id": "GHSA-c3jg-qh8m-j3h2",
  "modified": "2026-10-08T19:41:22Z",
  "published": "2026-10-08T19:41:22Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/Kozea/CairoSVG/security/advisories/GHSA-c3jg-qh8m-j3h2"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-107378"
    },
    {
      "type": "WEB",
      "url": "https://github.com/Kozea/CairoSVG/commit/9d63f049f9988d0ddda3eb94564ac3a50a286523"
    },
    {
      "type": "WEB",
      "url": "https://github.com/Kozea/CairoSVG/commit/a4d585eb374724b79676e9cceaa9e9a1a4358565"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/Kozea/CairoSVG"
    },
    {
      "type": "WEB",
      "url": "https://github.com/Kozea/CairoSVG/releases/tag/2.9.1"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "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",
      "type": "CVSS_V4"
    }
  ],
  "summary": "CairoSVG: Quadratic-time DoS parsing a crafted SVG \u003cpath\u003e"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…