# Fabric.js Affected by Stored XSS via SVG Export

Fabric.js escapes text content but not user-controlled id/src values during SVG export, letting attacker JSON inject event handlers into exported SVG for stored XSS.

- Payout: Research
- Severity: high
- Program: Fabric.js
- Found by: nedlir
- Disclosed: 2026-02-18
- CVE: CVE-2026-27013
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-hfvx-25r5-qc3w
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-hfvx-25r5-qc3w
- Tags: area:web domain:oss domain:web impact:integrity kind:research platform:ghsa sev:high tech:nodejs technique:incomplete-fix technique:source-review vuln:xss

**Target:** npm fabric library SVG export (toSVG) and JSON deserialization (loadFromJSON)
**Root cause:** escapeXml() is applied to text content but not to user-controlled string properties (id, image src, pattern source/id, gradient id) interpolated into SVG attribute markup, so values break out of XML attributes.
**Steps:**
1. Craft JSON with a fabric object whose `id` contains an attribute-breaking payload, e.g. `id` value that closes the `id="..."` attribute and injects an SVG element with an event handler.
2. Load it via `loadFromJSON()`; `_fromObject()` passes all properties to the constructor and `_setOptions()` assigns every property verbatim via `this.set(prop, options[prop])` with no allowlist.
3. Call `toSVG()`; `getSvgCommons()` interpolates `id="${this.id}"` unescaped into the `<g>` wrapper for every object type.
4. Render the resulting SVG in a browser context (preview, export download, embed) to trigger the injected handler.
5. Alternate vectors: image `src` injected into `xlink:href` in Image `_toSVG()`, Pattern `sourceToString()`/`id` in `toSVG()`, Gradient user-supplied `id` prefix (constrained by `_<uid>` suffix).
**How found:** Code review noting the inconsistency: text content is passed through escapeXml() in TextSVGExportMixin.ts:186, but other user-controlled strings interpolated into SVG attributes are not; traced the deserialization path from loadFromJSON through _fromObject/_setOptions to confirm properties are set verbatim.
**Exploitation:** Attacker supplies malicious canvas JSON to an app that stores/shares it (collaborative design tool, CMS editor). Victim loads the JSON and the app renders the toSVG() output in-page, executing attacker JavaScript in the victim's session.
**Insight:** When a library escapes one output context (text nodes) but not another (attribute values), enumerate every user-controlled property that reaches attribute interpolation - here the generic `id` on the base mixin hits all object types, and constructors that Object.assign options after generating a uid let attackers overwrite it.

