Problem
SVG files are executable documents, not inert images. When users paste or import SVGs into tldraw, there's no sanitization step — meaning malicious SVGs with embedded scripts, cross-origin references, or phishing links can make it through to rendering.
Attack vectors in the current codebase
- SVG text pasting — User-pasted SVG text is parsed via
DOMParser and stored as an asset with no sanitization (defaultExternalContentHandlers.ts:314)
- SVG file drops — SVG files dropped onto the canvas are handled as image assets without content inspection
- Bookmark favicons — Favicon URLs extracted from fetched pages could point to malicious SVGs (
BookmarkShapeUtil.tsx:216)
- SVG export round-trips — Exported SVGs re-imported could carry injected content
While <img> tags don't execute scripts, SVGs opened as top-level documents or injected via other means can.
Proposed solution
Add SVG sanitization as a defense-in-depth measure in the SDK. One strong candidate is svg-hush from Cloudflare:
- Allowlist-based approach — removes elements/attributes not explicitly permitted
- Strips scripting, cross-domain links, and cross-origin resource references
- Written in Rust (could compile to WASM for browser use)
- MIT licensed, actively maintained
Where to sanitize
- In
defaultExternalContentHandlers.ts before SVG text is stored as an asset
- In the asset creation pipeline for SVG files
- Optionally as a configurable
sanitizeSvg hook so SDK users can customize
Alternatives
- DOMPurify — widely used HTML/SVG sanitizer, pure JS, larger scope than needed
- Manual strip — remove
<script>, event attributes, etc. manually (error-prone, incomplete)
- CSP headers only — helps but is a dotcom-level mitigation, not an SDK-level one
Considerations
- Bundle size impact of WASM module vs. JS-only solution
- Whether sanitization should be opt-in or on-by-default
- Performance impact on large/complex SVGs
- Need to preserve valid SVG features (filters, gradients, masks) while stripping dangerous ones
Problem
SVG files are executable documents, not inert images. When users paste or import SVGs into tldraw, there's no sanitization step — meaning malicious SVGs with embedded scripts, cross-origin references, or phishing links can make it through to rendering.
Attack vectors in the current codebase
DOMParserand stored as an asset with no sanitization (defaultExternalContentHandlers.ts:314)BookmarkShapeUtil.tsx:216)While
<img>tags don't execute scripts, SVGs opened as top-level documents or injected via other means can.Proposed solution
Add SVG sanitization as a defense-in-depth measure in the SDK. One strong candidate is svg-hush from Cloudflare:
Where to sanitize
defaultExternalContentHandlers.tsbefore SVG text is stored as an assetsanitizeSvghook so SDK users can customizeAlternatives
<script>, event attributes, etc. manually (error-prone, incomplete)Considerations