Fabric.js Affected by Stored XSS via SVG Export
Date: 2026-02
Affected: npm/fabric
Attack vector: deserialization
Category: vulnerability-disclosure
Severity: High
CVE: CVE-2026-27013
CWE: CWE-116
CWE-79
CVSS: 7.6 (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:L)

fabric.js applies `escapeXml()` to text content during SVG export (`src/shapes/Text/TextSVGExportMixin.ts:186`) but fails to apply it to other user-controlled string values that are interpolated into SVG attribute markup. When attacker-controlled JSON is loaded via `loadFromJSON()` and later exported via `toSVG()`, the unescaped values break out of XML attributes and inject arbitrary SVG elements including event handlers. ### Deserialization Path (no sanitization) `loadFromJSON()` (`src/canvas/StaticCanvas.ts:1229`) calls `enlivenObjects()` which calls `_fromObject()` (`src/shapes/Object/Object.ts:1902`). `_fromObject` passes all deserialized properties to the shape constructor via `new this(enlivedObjectOptions)`. The constructor ultimately calls `_setOptions()` (`src/CommonMethods.ts:9`) which iterates over every property and assigns it to the object via `this.set(prop, options[prop])`. There is no allowlist or sanitization - any property in the JSON, including `id`, is set verbatim on the fabric object. --- ### Finding 1: XSS via `id` Property Injection The `id` property from deserialized JSON is interpolated directly into SVG attribute strings without escaping. **Vulnerable code (`src/shapes/Object/FabricObjectSVGExportMixin.ts`, line 89, `getSvgCommons()`):** ```typescript getSvgCommons( this: FabricObjectSVGExportMixin & FabricObject & { id?: string }, ) { return [ this.id ? `id="${this.id}" ` : '', // <-- unescaped, user-controlled this.clipPath


Mitigations:

Architectural layers (MAESTRO):