Skip to content

✅ test(clean): validate sanitizer against DOMPurify suite#583

Merged
gaborbernat merged 3 commits into
tox-dev:mainfrom
gaborbernat:test/sanitizer-dompurify-conformance
Jul 7, 2026
Merged

✅ test(clean): validate sanitizer against DOMPurify suite#583
gaborbernat merged 3 commits into
tox-dev:mainfrom
gaborbernat:test/sanitizer-dompurify-conformance

Conversation

@gaborbernat

Copy link
Copy Markdown
Member

The sanitizer's shipped DOMPurify-parity features (SAFE_FOR_TEMPLATES, SANITIZE_NAMED_PROPS, CUSTOM_ELEMENT_HANDLING, USE_PROFILES) were covered only by turbohtml's own hand-written expectations. Green coverage is not the same as spec-correctness, so this validates them against the authority: DOMPurify's own test corpus and a live DOMPurify build.

DOMPurify is vendored as a pinned shallow submodule at tests/conformance/DOMPurify, and tests/conformance/test_sanitizer_dompurify_conformance.py runs every payload in its test/fixtures/expect.mjs (~219 XSS vectors and config cases) through turbohtml.clean.sanitize. 🔐 The oracle is DOMPurify's actual output: a batch Node runner (tools/bench/node/dompurify_conformance_runner.js, reusing the existing isomorphic-dompurify dependency) sanitizes the same inputs under the config that mirrors each Policy feature, so the comparison is head-to-head rather than against a frozen string. The absolute security invariant is asserted hard over the whole corpus under every config: turbohtml's output reparses to an inert tree, and it never keeps an executable construct DOMPurify strips. Because DOMPurify keeps nearly all of HTML by default while a Policy is an explicit allowlist, byte-parity over arbitrary payloads is dominated by allowlist scope rather than the feature under test, so each feature is also validated for exact canonical parity on curated inputs whose tags both allowlists share, where the only variable is the config flag.

Two divergences are documented and xfailed rather than hidden, both cases where turbohtml is stricter and so never less safe: the template stripper collapses only marker runs that can open a template evaluation ({{/${/<%), leaving a bare unmatched close delimiter as inert text, while DOMPurify's greedy regex also eats the text before it; and stripping a disallowed HTML wrapper drops a nested foreign (SVG/MathML) subtree instead of hoisting it. ✨ No sanitize.c change was needed: the corpus found no case where turbohtml is less safe or diverges on a feature it claims to support. The harness carries no coverage (it is in run.omit, like the other differential suites) and skips cleanly when the submodule or Node build is absent.

validates #527 #531 #533 #534 #536

@gaborbernat gaborbernat added the enhancement New feature or request label Jul 7, 2026
@codspeed-hq

codspeed-hq Bot commented Jul 7, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 83 untouched benchmarks
⏩ 18 skipped benchmarks1


Comparing gaborbernat:test/sanitizer-dompurify-conformance (69e22ea) with main (db0a1e6)

Open in CodSpeed

Footnotes

  1. 18 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@gaborbernat gaborbernat force-pushed the test/sanitizer-dompurify-conformance branch 6 times, most recently from e2d0307 to a7412d2 Compare July 7, 2026 19:52
Conformance suites and the bug fixes they surface carry no changelog;
the feature's own fragment covers the capability.
@gaborbernat gaborbernat force-pushed the test/sanitizer-dompurify-conformance branch from a7412d2 to 69e22ea Compare July 7, 2026 22:25
@gaborbernat gaborbernat merged commit 62da484 into tox-dev:main Jul 7, 2026
49 checks passed
@gaborbernat gaborbernat deleted the test/sanitizer-dompurify-conformance branch July 10, 2026 17:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant