On this page (7 sections)
A GitHub-reviewed security advisory describes a bypass in DOMPurify, the widely used JavaScript HTML sanitizer. When the SAFE_FOR_TEMPLATES option is combined with a DOM-returning output mode, template expressions can survive sanitization inside the content of an attached shadow root. The advisory is rated critical and the fix is in dompurify 3.4.17.
If your front end, or a site builder or plugin that bundles DOMPurify, uses this combination, you should check your version. Most sites will not be affected, because the advisory says exploitation needs a non-default configuration. Coverage so far is thin: the GitHub advisory is the only source in our research, and it lists no CVE identifier or CVSS score.
What happened
The advisory was published on October 12, 2026 in the cure53/DOMPurify repository and updated on October 22, 2026. It says that with SAFE_FOR_TEMPLATES set to true and one of RETURN_DOM, RETURN_DOM_FRAGMENT or IN_PLACE enabled, expressions such as {{...}}, ${...} and <%...%> can remain in the text of an attached shadow root after sanitizing.
The patched release is 3.4.17. The affected range starts at 3.4.4, so older releases are not listed as affected by this advisory.
Why the bypass works
SAFE_FOR_TEMPLATES exists for apps that pass sanitized output into a template engine. It removes template delimiters so the engine cannot evaluate attacker-controlled expressions. According to the advisory, it does this in two stages: a per-text-node strip during the main walk, and a final pass that merges adjacent text nodes and strips any expression that only appears after merging.
The weakness is in the second stage. An expression split into fragments looks harmless piece by piece, so the first stage misses it. If the markup between the fragments is disallowed and gets removed, the fragments become adjacent. The final pass would catch that, but the advisory says it cannot reach shadow root content: normalization does not merge text nodes inside shadow roots, the iterator does not descend into shadow trees, and the explicit recursion only covers template content. Shadow roots are therefore checked only node by node, never as a merged whole.
In plain terms, this is an incomplete-sanitization problem: a safety net that covers most of the document tree but skips one part of it. We are not reproducing the advisory's proof of concept here.
Who is realistically exposed
The advisory is explicit that exploitation requires non-default configuration. All of the following must be true:
- The application enables SAFE_FOR_TEMPLATES.
- It uses a DOM-returning mode: RETURN_DOM, RETURN_DOM_FRAGMENT or IN_PLACE.
- It passes a DOM node that already has an attached shadow root to the sanitizer.
- It then feeds the sanitized shadow root content into a template evaluator.
The advisory adds that the shadow root does not have to be built in code. In browsers that support declarative shadow DOM, attacker-supplied markup containing a template element with shadowrootmode set can produce the same attached shadow root when parsed through setHTMLUnsafe or Document.parseHTMLUnsafe. So a plain HTML string can reach the gap once the application is configured this way.
The impact depends on the downstream engine. The advisory says client-side contexts can yield cross-site scripting, and a server-side template context can escalate to template injection. It also states that the actual consequences depend on the engine's evaluation context.
How to check your exposure
First find out which dompurify version your project resolves. DOMPurify is often a transitive dependency, so check the full tree rather than only your package.json.
npm ls dompurifyIf your project uses Yarn or pnpm, the equivalent read-only queries are below.
yarn why dompurify
pnpm why dompurifyNext, search your own code for the risky combination. This read-only search lists files that mention the options named in the advisory; any file that matches both SAFE_FOR_TEMPLATES and a DOM-returning option deserves a manual review.
grep -rnE "SAFE_FOR_TEMPLATES|RETURN_DOM|IN_PLACE" --include=*.js --include=*.ts --include=*.jsx --include=*.tsx --exclude-dir=node_modules .Also check any built or bundled JavaScript you ship, since a vendored copy of DOMPurify will not show up in npm output. Third-party themes, plugins and widgets that bundle their own copy are worth asking vendors about.
What to do now
0 of 7 done
npm install dompurify@3.4.17
npm ls dompurifyAfter the upgrade, the second command should show 3.4.17 for every path. If an older copy still appears under another package, update that parent package or use your package manager's override mechanism, then re-check.
Defense in depth and monitoring
A sanitizer should not be your only barrier. A strict Content-Security-Policy limits what injected script can do if a bypass like this slips through, and you can review yours with the free security headers checker. Our guide to security headers explains the options.
Beyond that, keep dependency auditing in your CI so advisories like this one surface automatically, and avoid passing sanitized output into template engines that evaluate expressions. For a broader look at what to watch on live sites, see our guide on checking whether a website is hacked.
What we do not know yet
- No CVE identifier or CVSS score appears in the material we reviewed.
- The advisory does not say whether the issue is exploited in the wild.
- Only one source covers this so far, so details such as exact scope may be refined as maintainers or others publish more.
Treat the advisory's version range and fixed version as the working guidance, and re-check the advisory page for updates.
Sources
This article is based on the public sources below. Details can change as vendors and researchers publish more; check the original advisories before acting on version numbers.
