GitHub - ppmpreetham/DOMOxide: Prevent XSS Attacks, in the speed of light (~70x faster)!

GitHub

2 min read Original article ↗

DOMOxide is a high-performance Rust + WebAssembly HTML sanitizer that is ~70x faster than DOMPurify.

Installation

npm install domoxide
# or
pnpm add domoxide
# or
yarn add domoxide

Benchmarks

Tip

Try it yourself: pnpm run bench

payload input size DOMPurify DOMOxide speed up
tiny-xss 61 B ~1.0–1.5 ms/op ~22–35 μs/op ~45x
rich-text 167 B ~2.8–3.5 ms/op ~20–22 μs/op ~150x
svg-filter 199 B ~2.0–3.9 ms/op ~37–50 μs/op ~40-105x
upstream-fixture-long 1.3 KB ~4.9-7.2 ms/op ~34-85 μs/op ~85-145x
large-repeated-html 12.6 KB ~17.6–21.6 ms/op ~0.8–1.0 ms/op ~21x

Usage

There are a few ways:

Dompurify way

import { createDOMPurify } from "domoxide/compat";

// no window needed lol
const DOMPurify = await createDOMPurify();

DOMPurify.sanitize("<img src=x onerror=alert(1)>"); // '<img src="x">'
DOMPurify.sanitize(dirty, { FORBID_TAGS: ["style"], USE_PROFILES: { html: true } });

2. Bundlers (Vite, Next.js, Webpack, Rollup)

import { createDOMPurify } from "domoxide/compat";

const DOMPurify = await createDOMPurify();
export const sanitize = (html) => DOMPurify.sanitize(html);

3. Raw WebAssembly ESM Bindings

If you prefer using the raw WebAssembly exports directly:

import init, { sanitize_wasm } from "domoxide";
await init();

const clean = sanitize_wasm(dirty, {
  ALLOWED_TAGS: ["b", "i", "em", "strong", "a"],
  FORBID_ATTR: ["style"],
  USE_PROFILES: { html: true },
});

4. Node.js (ESM)

import { readFile } from "node:fs/promises";
import { createDOMPurify } from "domoxide/compat";

const wasm = await readFile(new URL("./node_modules/domoxide/domoxide_bg.wasm", import.meta.url));
const DOMPurify = await createDOMPurify({ module_or_path: wasm });

DOMPurify.sanitize("<img src=x onerror=alert(1)>"); // '<img src="x">'

5. Browser (No Bundler / ESM CDN)

<script type="module">
  import { createDOMPurify } from "https://esm.sh/domoxide/compat";

  const DOMPurify = await createDOMPurify();
  document.body.innerHTML = DOMPurify.sanitize(userInput);
</script>

Parity

218 of 219 test cases from DOMPurify's test suite pass identically.

Note

The 219th is a documented IE-era isindex ordering quirk that no modern engine (including jsdom DOMPurify) reproduces; DOMOxide adheres strictly to HTML5 spec order.