SvgSanitizer

Vetted SVG sanitizer for Elixir. Thin precompiled NIF wrapping Cloudflare's svg-hush Rust crate, so applications can accept user-uploaded SVG without authoring their own allowlist scrubber — <script>, event handlers, foreign objects, external references, and javascript:/data: URL vectors are stripped, raster image data URLs (PNG / JPEG / GIF / WebP) are preserved so sanitized SVGs stay self-contained.

The NIF runs on a dirty CPU scheduler and converts Rust panics into {:error, term} rather than crashing the BEAM node.

Installation

def deps do
  [
    {:svg_sanitizer, "~> 0.1"}
  ]
end

Precompiled artifacts are published to GitHub releases for these targets; mix deps.get downloads the right one — no Rust toolchain required:

OTP 26+ required (NIF 2.17). Earlier NIF versions will be added on demand.

macOS users: v0.1 doesn't ship precompiled macOS artifacts (the rustler-precompiled-action mishandles cross on Apple Silicon; tracked for v0.2). Build from source by setting SVG_SANITIZER_BUILD=1; you'll need cargo installed.

Usage

case SvgSanitizer.sanitize(user_uploaded_svg) do
  {:ok, clean} ->
    store_asset(clean)

  {:error, reason} ->
    # reason is one of:
    #   :invalid_input    — input wasn't a binary
    #   :input_too_large  — over 5 MB; rejected without parsing
    #   :parse_error      — svg-hush rejected as malformed
    #   :panic            — Rust layer panicked (caught, BEAM safe)
    #   :alloc_failed     — out of memory while building output
    Logger.warning("svg sanitize failed: #{reason}")
    reject_upload(reason)
end

Always handle {:error, _}. SVG input from users will hit one of those branches eventually; pattern-matching only on {:ok, _} is a MatchError waiting to happen.

Why a separate package

Elixir has no purpose-built SVG sanitizer; html_sanitize_ex is HTML-only and pointing it at SVG amounts to hand-rolling an allowlist (which is what makes SVG sanitization dangerous — the TYPO3 PSA-2025-001 advisory traces a real-world bypass to exactly that). svg-hush is Cloudflare-maintained, allowlist-based, and security-focused; this package is the thin glue that makes it available to Phoenix / Plug / LiveView applications.

License

Apache-2.0 — matches the upstream svg-hush crate.