{"_id":"@bytevet/htmlsanitizer","_rev":"2-e6f2c773ba9d32ddc4afbff8af7d1213","name":"@bytevet/htmlsanitizer","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@bytevet/htmlsanitizer","version":"0.1.0","keywords":["html","sanitizer","xss","security","wasm","webassembly"],"license":"MIT","_id":"@bytevet/htmlsanitizer@0.1.0","maintainers":[{"name":"symollin","email":"symollin@gmail.com"}],"dist":{"shasum":"ac74a7f953224c5d520669d0ac3fd16d5bcd86b8","tarball":"https://registry.npmjs.org/@bytevet/htmlsanitizer/-/htmlsanitizer-0.1.0.tgz","fileCount":13,"integrity":"sha512-+R5JxZX8kPYXTJD+N6QRFndDv5QWPESDm/S6maQZ2BtDKWkx5vRTaUJywxEp1A6lgEvuNzlRdOpS18qYO6MoLg==","signatures":[{"sig":"MEUCIQDaOz5mleeDFJjbyBX+B13RIfGydCo4lm/84yqnfQMCsgIgLFT64l4CyEaRFGEnpxcU5N12lyMnxsxL0fM1ezM+BVQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":265027},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"979727dab9cdbffac4fa38bf1f0b2b44a92b5759","scripts":{"test":"vitest run","build":"npm run build:wasm && npm run build:ts","build:ts":"tsc","build:wasm":"wasm-pack build .. --target bundler --out-dir npm/pkg -- --features wasm","test:watch":"vitest"},"_npmUser":{"name":"symollin","email":"symollin@gmail.com"},"_npmVersion":"11.9.0","description":"A fast, allowlist-based HTML sanitizer powered by WebAssembly","directories":{},"_nodeVersion":"25.6.1","_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.4.0","vite-plugin-wasm":"^3.6.0","vite-plugin-top-level-await":"^1.6.0"},"_npmOperationalInternal":{"tmp":"tmp/htmlsanitizer_0.1.0_1774588721053_0.412678144931935","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@bytevet/htmlsanitizer","version":"0.2.0","description":"A fast, allowlist-based HTML sanitizer powered by WebAssembly","license":"MIT","repository":{"type":"git","url":"git+https://github.com/SYM01/htmlsanitizer-rs.git"},"type":"module","main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"}},"scripts":{"build:wasm":"wasm-pack build .. --target bundler --out-dir npm/pkg -- --features wasm","build:ts":"tsc","build":"npm run build:wasm && npm run build:ts","test":"vitest run","test:watch":"vitest"},"devDependencies":{"typescript":"^5.4.0","vite-plugin-top-level-await":"^1.6.0","vite-plugin-wasm":"^3.6.0","vitest":"^3.0.0"},"keywords":["html","sanitizer","xss","security","wasm","webassembly"],"gitHead":"66e35a73f04de00dc95d56cc9de84ec3b0b69850","_id":"@bytevet/htmlsanitizer@0.2.0","bugs":{"url":"https://github.com/SYM01/htmlsanitizer-rs/issues"},"homepage":"https://github.com/SYM01/htmlsanitizer-rs#readme","_nodeVersion":"24.14.0","_npmVersion":"11.9.0","dist":{"integrity":"sha512-y6jSq/TzSIlnkGzHFsMj4eqYw4htOh0W09Pviz3hHh2dJA4QiRUlBXnOY9jxQgpdZSQpmqiW0UCjLroFJZRBkQ==","shasum":"be862bee98d168ecc7686e6295f71b5d0b8e902e","tarball":"https://registry.npmjs.org/@bytevet/htmlsanitizer/-/htmlsanitizer-0.2.0.tgz","fileCount":14,"unpackedSize":275787,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bytevet%2fhtmlsanitizer@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDuvRCxzDUHO8TiKtcS2t6Ec5TNO5a66l560yH50z/gsgIhAJ1guI/g4Q1PArH/1+/KxFIMxSSIIshIdnuDllwsHQOF"}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:66e6c209-6fcf-4b48-8c27-57c462e09ad0"}},"directories":{},"maintainers":[{"name":"symollin","email":"symollin@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/htmlsanitizer_0.2.0_1774594311754_0.20838919514746235"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-27T05:18:40.996Z","modified":"2026-03-27T06:51:52.179Z","0.1.0":"2026-03-27T05:18:41.245Z","0.2.0":"2026-03-27T06:51:51.901Z"},"license":"MIT","keywords":["html","sanitizer","xss","security","wasm","webassembly"],"description":"A fast, allowlist-based HTML sanitizer powered by WebAssembly","maintainers":[{"name":"symollin","email":"symollin@gmail.com"}],"readme":"# @bytevet/htmlsanitizer\n\n[![npm](https://img.shields.io/npm/v/@bytevet/htmlsanitizer)](https://www.npmjs.com/package/@bytevet/htmlsanitizer)\n[![CI](https://github.com/SYM01/htmlsanitizer-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/SYM01/htmlsanitizer-rs/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/SYM01/htmlsanitizer-rs/blob/main/LICENSE)\n\nA fast, allowlist-based HTML sanitizer powered by WebAssembly. [**3.7–44x faster**](#performance) than DOMPurify on real HTML content.\n\nShips a pre-built WASM binary — no native toolchain required.\n\nAlso available in [Rust](https://crates.io/crates/htmlsanitizer) and [Go](https://github.com/SYM01/htmlsanitizer).\n\n## Features\n\n- **O(n) streaming parser** — DFA-based finite state machine; no DOM tree, no backtracking\n- **Allowlist-based** — only explicitly permitted tags and attributes pass through; everything else is stripped\n- **URL sanitization** — rejects `javascript:`, `data:`, `ftp:`, control characters, and opaque URIs\n- **Customizable** — add/remove tags, modify allowed attributes\n- **Identical output** to the native Rust crate — same sanitization engine compiled to WASM\n\n## Installation\n\n```bash\nnpm install @bytevet/htmlsanitizer\n```\n\n## Quick Start\n\n```ts\nimport { sanitize } from \"@bytevet/htmlsanitizer\";\n\nconst safe = sanitize('<p>Hello</p><script>alert(\"xss\")</script>');\n// => \"<p>Hello</p>\"\n```\n\n## Usage\n\n### Default Sanitization\n\n```ts\nimport { sanitize } from \"@bytevet/htmlsanitizer\";\n\nsanitize('<img src=x onerror=\"alert(1)\">');\n// => '<img src=\"x\">'\n\nsanitize('<a href=\"javascript:alert(1)\">click</a>');\n// => '<a>click</a>'\n```\n\n### Custom Configuration\n\n```ts\nimport { HtmlSanitizer } from \"@bytevet/htmlsanitizer\";\n\nconst s = new HtmlSanitizer();\n\n// Remove a tag from the allow list\ns.removeTag(\"a\");\ns.sanitize('<a href=\"http://example.com\">link</a>');\n// => \"link\"\n\n// Add a custom tag\n// Arguments: name, comma-separated attributes, comma-separated URL attributes\ns.addTag(\"custom-el\", \"data-x,title\", \"href\");\ns.sanitize('<custom-el data-x=\"1\" onclick=\"bad\">content</custom-el>');\n// => '<custom-el data-x=\"1\">content</custom-el>'\n\n// Add a global attribute (allowed on all tags)\ns.addGlobalAttr(\"data-testid\");\n\n// Release WASM memory when done (instance is unusable after this)\ns.free();\n```\n\n## API Reference\n\n| Export | Description |\n|---|---|\n| `sanitize(input: string): string` | One-shot sanitization with the default allow list |\n| `new HtmlSanitizer()` | Create a configurable sanitizer instance |\n| `.sanitize(input: string): string` | Sanitize HTML using the instance's configuration |\n| `.addTag(name, attrs?, urlAttrs?)` | Add a tag; `attrs` and `urlAttrs` are comma-separated strings |\n| `.removeTag(name: string)` | Remove a tag from the allow list |\n| `.addGlobalAttr(name: string)` | Allow an attribute on all tags |\n| `.free()` | Release WASM memory; the instance is unusable after this |\n\n## Default Allow List\n\nThe default allow list permits 68 commonly used HTML tags. All other tags are stripped — their text content is preserved. Tags in the non-HTML list (`script`, `style`, `object`) have both their tags **and** content removed.\n\n**Global attributes** (allowed on every permitted tag): `class`, `id`\n\n<details>\n<summary>View all 68 default tags</summary>\n\n| Category | Tags |\n|---|---|\n| Structural | `address`, `article`, `aside`, `footer`, `header`, `h1`–`h6`, `hgroup`, `main`, `nav`, `section` |\n| Block content | `blockquote`, `dd`, `div`, `dl`, `dt`, `figcaption`, `figure`, `hr`, `li`, `ol`, `p`, `pre`, `ul` |\n| Inline text | `a`, `abbr`, `b`, `bdi`, `bdo`, `br`, `cite`, `code`, `data`, `em`, `i`, `kbd`, `mark`, `q`, `s`, `small`, `span`, `strong`, `sub`, `sup`, `time`, `u` |\n| Media | `area`, `audio`, `img`, `map`, `track`, `video`, `picture`, `source` |\n| Table | `caption`, `col`, `colgroup`, `table`, `tbody`, `td`, `tfoot`, `th`, `thead`, `tr` |\n| Edit marks | `del`, `ins` |\n| Interactive | `details`, `summary` |\n\n**Notable tag-specific attributes:**\n\n| Tag | Regular attributes | URL attributes |\n|---|---|---|\n| `a` | `rel`, `target`, `referrerpolicy` | `href` |\n| `img` | `alt`, `crossorigin`, `height`, `width`, `loading`, `referrerpolicy` | `src` |\n| `video` | `autoplay`, `buffered`, `controls`, `crossorigin`, `duration`, `loop`, `muted`, `preload`, `height`, `width` | `src`, `poster` |\n| `audio` | `autoplay`, `controls`, `crossorigin`, `duration`, `loop`, `muted`, `preload` | `src` |\n| `td` / `th` | `colspan`, `rowspan` (+ `scope` for `th`) | — |\n\n</details>\n\n## URL Sanitization\n\nAttributes marked as URL attributes (`href`, `src`, `poster`, `cite`, etc.) are validated by the URL sanitizer. The default behavior:\n\n**Accepted:**\n- `http://` and `https://` URLs\n- Relative URLs (paths, fragments, query strings)\n\n**Rejected:**\n- `javascript:` (including case variations and HTML-entity-encoded forms)\n- `data:` URIs\n- `ftp:` and all other non-HTTP schemes\n- URLs containing ASCII control characters (bytes < 0x20 or 0x7F)\n- Opaque (cannot-be-a-base) URIs\n- Percent-encoded ASCII in hostnames\n\nWhen a URL is rejected, the attribute is removed but the tag and its content are preserved (e.g., `<a href=\"javascript:...\">text</a>` becomes `<a>text</a>`).\n\n## Security Considerations\n\n- **Defense in depth.** This sanitizer is designed as one layer of an XSS mitigation strategy. Combine it with Content Security Policy headers and context-aware output encoding.\n- **Not a full HTML parser.** The DFA-based approach handles real-world HTML effectively but does not build a DOM tree. It is designed to be conservative — when in doubt, content is stripped.\n- **Fuzz-tested.** The project includes a `cargo-fuzz` harness. If you discover a bypass, please report it via [GitHub Issues](https://github.com/SYM01/htmlsanitizer-rs/issues).\n- **Tested against known XSS vectors.** The test suite includes vectors from OWASP and other common XSS payloads.\n\n## Performance\n\nBenchmarked with [Vitest bench](https://vitest.dev/guide/features#benchmarking) on Node.js against [DOMPurify](https://github.com/cure53/DOMPurify) (with jsdom):\n\n| Payload | @bytevet/htmlsanitizer | DOMPurify + jsdom | Ratio |\n|---|---|---|---|\n| Simple HTML (small) | 56,716 ops/s | 15,253 ops/s | **3.7x faster** |\n| XSS vectors | 40,908 ops/s | 5,373 ops/s | **7.6x faster** |\n| Blog post (medium) | 33,259 ops/s | 1,381 ops/s | **24x faster** |\n| Mixed safe + dangerous | 40,326 ops/s | 3,987 ops/s | **10x faster** |\n| Large document (~50 KB) | 1,054 ops/s | 24 ops/s | **44x faster** |\n\n> DOMPurify is faster on tiny plain-text inputs (no HTML tags) due to WASM call overhead (~10 µs). For any real HTML content, `@bytevet/htmlsanitizer` is **3.7–44x faster**, with the advantage growing as input size increases.\n\n## License\n\nMIT — see [LICENSE](https://github.com/SYM01/htmlsanitizer-rs/blob/main/LICENSE).\n","readmeFilename":"README.md","homepage":"https://github.com/SYM01/htmlsanitizer-rs#readme","repository":{"type":"git","url":"git+https://github.com/SYM01/htmlsanitizer-rs.git"},"bugs":{"url":"https://github.com/SYM01/htmlsanitizer-rs/issues"}}