You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
While fixing #194 (PR #201) we found that happy-dom cannot be trusted to test DOMPurify-based sanitization — it can make a broken sanitizer appear to work.
Details
DOMPurify sanitizes by walking the DOM with document.createNodeIterator(...) and removing disallowed nodes in place, mid-walk. The DOM spec requires a NodeIterator to adjust its position when the current node is removed (live-mutation adjustment). happy-dom's NodeIterator does not implement this — once DOMPurify removes the first disallowed node, iteration silently stops, and every node after it is left unsanitized.
With a correct config, <img onerror><img onerror><script>evil()</script> left the 2nd onerror img and the <script>unsanitized under happy-dom.
This is a test-environment soundness problem — production runs in a real browser with a spec-compliant NodeIterator, so the shipped sanitizer is fine. But any test that validates DOMPurify behavior under happy-dom may be giving false security assurances.
frontend/workspace uses @happy-dom/global-registrator (frontend/workspace/happydom.ts). Audit whether it hosts any DOMPurify-based (or other mid-walk-mutation) tests and, if so, move them to jsdom or otherwise make them sound. Single-node XSS payloads pass even under the broken engine, so a passing test is not evidence of soundness — use a multi-node payload as a canary.
Summary
While fixing #194 (PR #201) we found that happy-dom cannot be trusted to test DOMPurify-based sanitization — it can make a broken sanitizer appear to work.
Details
DOMPurify sanitizes by walking the DOM with
document.createNodeIterator(...)and removing disallowed nodes in place, mid-walk. The DOM spec requires a NodeIterator to adjust its position when the current node is removed (live-mutation adjustment). happy-dom'sNodeIteratordoes not implement this — once DOMPurify removes the first disallowed node, iteration silently stops, and every node after it is left unsanitized.Reproduced (both
@happy-dom/[email protected]and20.11.0):["A"]under happy-dom vs["A","B","C","D"]under jsdom.<img onerror><img onerror><script>evil()</script>left the 2ndonerrorimg and the<script>unsanitized under happy-dom.This is a test-environment soundness problem — production runs in a real browser with a spec-compliant NodeIterator, so the shipped sanitizer is fine. But any test that validates DOMPurify behavior under happy-dom may be giving false security assurances.
Impact / action
frontend/uisanitizer test to jsdom (addedjsdom/global-jsdom/@types/jsdomdevDeps).frontend/workspaceuses@happy-dom/global-registrator(frontend/workspace/happydom.ts). Audit whether it hosts any DOMPurify-based (or other mid-walk-mutation) tests and, if so, move them to jsdom or otherwise make them sound. Single-node XSS payloads pass even under the broken engine, so a passing test is not evidence of soundness — use a multi-node payload as a canary.Ref: PR #201, issue #194.