feat(tldraw): sanitize SVG content with DOMPurify#7880
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
5 Skipped Deployments
|
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ❌ Deployment failed View logs |
image-pipeline-template | 4cb539b | Feb 11 2026, 01:09 PM |
Replace the DOMPurify dependency with an in-house SVG sanitizer and lazy-load it where needed. dompurify was removed from packages/tldraw/package.json (and corresponding yarn.lock entries). The sanitizer implementation (packages/tldraw/src/lib/utils/svg/sanitizeSvg.ts) now parses SVG via DOMParser, whitelists allowed tags and attributes, validates URI attributes (blocking javascript:, data:, etc.), strips invisible whitespace used to bypass checks, and removes parse-error results. Call sites in defaultExternalContentHandlers now dynamically import defaultSanitizeSvg (reducing initial bundle size) and the file upload sanitizer was updated to use the new function.
Sanitize root SVG element attributes to prevent XSS via onload handlers. Strip url() and @import from CSS in style elements and attributes. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Prevents fully-malicious SVGs like <svg><script>alert(1)</script></svg> from passing through as empty <svg/> shells. Co-Authored-By: Claude Opus 4.6 <[email protected]>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
| 'fespotlight', | ||
| 'fetile', | ||
| 'feturbulence', | ||
| ]) |
There was a problem hiding this comment.
Missing animate tag from SVG allowlist
Medium Severity
ALLOWED_TAGS includes animatecolor, animatemotion, and animatetransform but omits the base animate element — the most commonly used SVG animation primitive (e.g., <animate attributeName="opacity" ...>). The comment states this list is "Based on DOMPurify's SVG + SVG filter profiles," and DOMPurify does include animate. Valid SVGs using <animate> will silently have their animations stripped.
There was a problem hiding this comment.
animate is included in the disallow list. bad bot 😡
Reverts #7880 (SVG sanitization). Our SVG export relies on `<foreignObject>` for text rendering (used by `RichTextSVG` in text, geo, note, arrow shapes + the fallback HTML renderer in `getSvgJsx`). The sanitizer correctly strips `foreignObject` (DOMPurify does the same — it's in their `svgDisallowed` list), but this breaks copy/paste of our own SVGs since all text content is lost. Keeps the docs recommendation to host assets on a separate domain — that's the primary defense against same-origin SVG attacks. Closes #7876 ### Change type - [x] `improvement` ### Test plan 1. Copy as SVG, paste into tldraw — text should render correctly with fonts 2. Drop SVG files onto canvas — should work as before - [ ] Unit tests - [ ] End to end tests ### Release notes - Revert SVG sanitization that broke copy/paste of SVGs containing text
Add SVG sanitization using an allowlist derived from DOMPurify's SVG profile. Strips disallowed elements and attributes, blocks javascript: and data: URIs, and returns clean SVG markup. Applied automatically through default external content handlers on paste, drop, and file insert. Upstream: tldraw#7880


Closes #7876
SVG files pasted/dropped into tldraw had no sanitization. The main risk is
defaultHandleExternalSvgTextContent()which parses SVG with DOMParser, temporarily appends todocument.bodyfor measurement (potential script execution), and stores raw SVG text as an asset. While<img>rendering sandboxes scripts, SVGs served from the same origin as the host app (e.g. via a same-domain asset store) can bypass this and execute arbitrary code. This PR adds defense-in-depth sanitization using DOMPurify — the industry-standard XSS sanitizer with first-class SVG support.Sanitization is applied unconditionally in all SVG entry points:
defaultHandleExternalSvgTextContent— SVG text pastingdefaultHandleExternalFileAsset— SVG file dropsdefaultHandleExternalFileContent— multi-file dropsdefaultHandleExternalFileReplaceContent— image replacementDOMPurify config uses
USE_PROFILES: { svg: true, svgFilters: true }which allows safe SVG elements/attributes (including filter primitives likefeGaussianBlur,feDropShadow, etc.) while stripping scripts, event handlers,javascript:URLs,foreignObject, etc.<use>andxlink:hrefare preserved for valid SVG features.If sanitization strips all content (fully malicious SVG), the file is rejected with a clear error rather than silently creating a broken empty asset.
Bundle size impact: ~9 KB gzipped additional transfer for end users (DOMPurify is 62 KB raw / 17 KB gzipped).
Change type
improvementTest plan
yarn dev, paste SVG with<script>alert(1)</script>— should render without scriptonload="alert(1)"attribute — should render without attributeRelease notes
Note
Medium Risk
Touches external content ingestion and asset upload/replace flows, so regressions could affect SVG handling or asset hashing/identity. Security logic changes are beneficial but must be correct to avoid bypasses or false positives that reject valid SVGs.
Overview
Adds SVG sanitization to default external content ingestion paths so pasted SVG text and dropped/replaced SVG files are cleaned before parsing, hashing, previewing, uploading, or being written to the store; SVGs that sanitize down to no usable content are rejected.
Introduces a new internal sanitizer (
utils/svg/sanitizeSvg.ts) with DOMPurify-like allowlists, URI protocol filtering, and basic CSSurl()/@importstripping, and updates the Assets docs with a new Security section recommending separate-domain asset hosting for defense in depth.Written by Cursor Bugbot for commit 4cb539b. This will update automatically on new commits. Configure here.