CVE-2026-27013: Verified Repro With Script Download
CVE-2026-27013: Fabric.js: Stored XSS via SVG Export
CVE-2026-27013 is verified against fabric · npm. Affected versions: < 7.2.0. Fixed in 7.2.0. Vulnerability class: XSS. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00105.
What Is CVE-2026-27013?
CVE-2026-27013 (GHSA-hfvx-25r5-qc3w) is a high-severity stored cross-site scripting (XSS) vulnerability in Fabric.js's SVG export functionality, present in versions before 7.2.0. Pruva reproduced it (reproduction REPRO-2026-00105).
CVE-2026-27013 Severity & CVSS Score
CVE-2026-27013 is rated high severity, with a CVSS base score of 7.6 out of 10.
High — serious impact or readily exploitable. Prioritize remediation.
Affected fabric Versions
fabric · npm versions < 7.2.0 are affected.
How to Reproduce CVE-2026-27013
pruva-verify REPRO-2026-00105 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00105/artifacts/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-27013
Reproduced by Pruva's autonomous agents — 97 tool calls over 16 min. Full root-cause analysis and the complete transcript are below.
How the agent worked
Root Cause and Exploit Chain for CVE-2026-27013
Summary
Fabric.js versions prior to 7.2.0 contain a stored Cross-Site Scripting (XSS) vulnerability in their SVG export functionality. When user-controlled JSON is loaded via loadFromJSON() and later exported via toSVG(), the id property and other user-controlled string values are interpolated directly into SVG attribute markup without XML escaping. This allows an attacker to break out of XML attributes and inject arbitrary SVG elements including event handlers, leading to stored XSS.
Impact
- Package: fabric (npm)
- Affected Versions:
< 7.2.0 - Fixed Version:
7.2.0 - CVE: CVE-2026-27013
- GHSA: GHSA-hfvx-25r5-qc3w
- CVSS Score: 7.6 (HIGH)
- CWEs: CWE-79 (XSS), CWE-116 (Improper Encoding or Escaping)
Risk and Consequences
Any application that:
- Accepts user-supplied JSON (via
loadFromJSON(), collaborative sharing, import features, CMS plugins) - Renders the
toSVG()output in a browser context (SVG preview, export download, email templates, embeds)
...is vulnerable to stored XSS. An attacker can execute arbitrary JavaScript in the victim's browser session. Real-world attack scenarios include:
- Collaborative design tools (Canva-like apps) where users share canvas state as JSON
- CMS or e-commerce platforms with fabric.js-based editors that store/render designs
- Any export-to-SVG workflow where the SVG is later displayed in a browser
Root Cause
The vulnerability exists because fabric.js fails to apply XML escaping to user-controlled string values that are interpolated into SVG attribute markup. While escapeXml() is correctly applied to text content during SVG export (in src/shapes/Text/TextSVGExportMixin.ts:186), it is NOT applied to other user-controlled string values.
Vulnerable Code Location
File: src/shapes/Object/FabricObjectSVGExportMixin.ts
Method: getSvgCommons() (line 85-97)
getSvgCommons(
this: FabricObjectSVGExportMixin & FabricObject & { id?: string },
) {
return [
this.id ? `id="${this.id}" ` : '', // <-- UNESCAPED, user-controlled
this.clipPath
? `clip-path="url(#${...})" `
: '',
].join('');
}
This method is called in _createBaseSVGMarkup() which wraps every object's SVG output in a <g> element. Since all fabric object types (Rect, Circle, Path, Text, Image, Group, etc.) inherit this mixin, the id injection vector applies to all object types.
Deserialization Path (No Sanitization)
loadFromJSON() (src/canvas/StaticCanvas.ts:1229) → enlivenObjects() → _fromObject() (src/shapes/Object/Object.ts:1902) → constructor → _setOptions() (src/CommonMethods.ts:9)
There is no allowlist or sanitization - any property in the JSON, including id, is set verbatim on the fabric object.
Fix Commit
The security fix was applied in commit: https://github.com/fabricjs/fabric.js/commit/7e1a122defd8feefe4eb7eaf0c180d7b0aeb6fee
The fix adds escapeXml() calls to all affected locations:
src/shapes/Object/FabricObjectSVGExportMixin.ts: Escapesthis.idsrc/shapes/Image.ts: EscapesgetSvgSrc()inxlink:hrefsrc/Pattern/Pattern.ts: Escapes patternidandsourceToString()src/gradient/Gradient.ts: Escapes gradientidsrc/Shadow.ts: Escapes shadowid
Reproduction Steps
Run the reproduction script:
./repro/reproduction_steps.sh
What the Script Does
- Clones fabric.js repository at the vulnerable version (7.1.0)
- Builds the project using npm
- Creates a Node.js test script that:
- Creates a
Rectobject with a maliciousidproperty containing XSS payload:""><script>alert(1)</script><rect id=" - Exports the object to SVG using
toSVG() - Checks if the payload is escaped or unescaped in the output
- Creates a
- Returns exit code 0 if vulnerable, 1 if patched
Expected Evidence of Reproduction
When vulnerable, the generated SVG contains the unescaped payload:
<g transform="matrix(1 0 0 1 0 0)" id=""><script>alert(1)</script><rect id="" >
The malicious id value breaks out of the id attribute and injects a <script> tag directly into the SVG markup.
Evidence
Reproduction Log
- Location:
logs/reproduction.log - Key Excerpt:
❌ VULNERABILITY CONFIRMED: Payload is NOT escaped in SVG output! The id attribute allows XSS injection via SVG export.
Generated Vulnerable SVG Output
<g transform="matrix(1 0 0 1 0 0)" id=""><script>alert(1)</script><rect id="" >
<rect style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill: rgb(255,0,0); fill-rule: nonzero; opacity: 1;" x="-50" y="-50" rx="0" ry="0" width="100" height="100" />
</g>
Environment Details
- Node.js Version: 22.22.0
- Fabric.js Version: 7.1.0 (vulnerable)
- OS: Linux
Recommendations / Next Steps
Immediate Actions
- Upgrade to fabric.js 7.2.0 or newer immediately
- Validate any stored JSON data that may have been created by untrusted users
- Sanitize SVG output if you cannot upgrade immediately (apply XML escaping to all interpolated values)
Testing Recommendations
- Add regression tests that verify XML escaping for all user-controlled properties
- Test SVG export with payloads containing:
",<,>,&,', and JavaScript event handlers - Consider using a content security policy (CSP) as defense-in-depth
Additional Affected Files (Per Ticket Analysis)
| File | Issue | Method |
|---|---|---|
src/shapes/Image.ts |
Unescaped getSvgSrc() in xlink:href |
_toSVG() |
src/Pattern/Pattern.ts |
Unescaped sourceToString() in xlink:href; unescaped id |
toSVG() |
src/gradient/Gradient.ts |
User-supplied id prefix interpolated unescaped |
toSVG() |
Additional Notes
Idempotency Confirmation
The reproduction script has been run twice consecutively with consistent results:
- Run 1: Vulnerability confirmed (exit code 0)
- Run 2: Vulnerability confirmed (exit code 0)
Edge Cases
- The vulnerability affects all fabric object types (Rect, Circle, Path, Text, Image, Group, etc.) through the shared
getSvgCommons()mixin - Multiple attack vectors exist:
idproperty, Imagesrc, Patternsource, Gradientid - The payload needs to break out of XML attribute context (
") and inject new elements
Limitations
This reproduction focuses on the primary id property injection vector. Other vectors (Image src, Pattern source, Gradient id) are also exploitable per the ticket analysis but were not explicitly tested in this reproduction script.
CVE-2026-27013 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
Artifacts and Evidence for CVE-2026-27013
Scripts, logs, diffs, and output captured during the reproduction.
No artifacts available
How to Fix CVE-2026-27013
Upgrade fabric · npm to 7.2.0 or later.
FAQ: CVE-2026-27013
How does the CVE-2026-27013 stored-XSS attack work?
id (or other string) property contains XML-breaking content. When that JSON is loaded via loadFromJSON() and later exported with toSVG(), the value is interpolated directly into SVG attribute markup without XML escaping, letting the attacker break out of the attribute and inject arbitrary SVG elements or event handlers that execute when the exported SVG is rendered in a browser.Which fabric.js versions are affected by CVE-2026-27013, and where is it fixed?
How severe is CVE-2026-27013?
How can I reproduce CVE-2026-27013?
id) via loadFromJSON(), exports it with toSVG(), and confirms the unescaped payload breaks out of the SVG attribute in the rendered output.References for CVE-2026-27013
Authoritative sources for CVE-2026-27013 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.