Technical Documentation

Excalidraw Security Review

Excalidraw Security Review

Threat model summary

ThreatControlLocation
Cross-user scene accessSupabase RLS on public.diagrams (owner_id = auth.uid())Migration diagrams policies
Plan bypassServer-side diagrams_enforce_free_limits trigger + entitlement check on every RPC that operates on whiteboardspublic.diagrams_enforce_free_limits
Secret leakage in scene JSONRedaction on save + on export via src/lib/security/scene-redaction.tsScene write path
Malicious SVG on library importsrc/lib/security/svg-sanitizer.ts strips <script>, event handlers, javascript: URLs, external entity refs, foreign contentLibrary import handler
XML external entity (XXE) on library importJSON-only parsing; SVG parsed with DOMParser and stripped, never document.writeLibrary import
Prototype pollution via scene restoreObject spread only; no Object.assign onto shared prototypes; scene is treated as dataserialize.ts
SSRF via image fetchImage URLs are internal signed URLs from Supabase Storage; user-provided remote URLs are refused during importImage handler
Clipboard HTML injectionHTML pasted into text elements is sanitized to plain textExcalidraw upstream + Falcon paste hook
Room link leakageNo hosted collaboration is enabled. Supabase Realtime rooms are gated by owner-only RLS on the diagram rowRealtime channel authorizer
Service-worker cache leakageNo service worker registered by Excalidraw in the reusable packageBundle boundary
PNG/SVG metadata leakageExport helpers do not embed session metadata; Falcon post-processes exports to strip Falcon metadata before downloadexcalidraw-board.tsx export path
Large scene DoSScene size cap (10 MB JSON, 5,000 elements Pro / 10,000 Pro Plus) enforced in trigger and before autosaveSave pipeline

CSP guidance

Recommend default-src 'self'; img-src 'self' data: blob: https://*.supabase.co; style-src 'self' 'unsafe-inline'; script-src 'self' 'wasm-unsafe-eval'; connect-src 'self' https://*.supabase.co wss://*.supabase.co; worker-src 'self' blob:;.

Trusted Types can be enabled once the marketing bundle no longer inlines runtime scripts.

What is intentionally allowed

  • data: and blob: URLs for image previews.
  • Inline SVG rendering (sanitized).

What is intentionally denied

  • Any AI network call other than the user-configured BYOK provider.
  • Any Firebase, Google Analytics, or Excalidraw-hosted endpoint.
  • Any remote font, script, or image outside Supabase Storage.