CSIRTS // UNIFIED SECURITY ADVISORY FEEDSYS ● ONLINE · POWERED BY INTELFUSIONS.COM

GHSA-gvmj-g25r-r7wr: DOMPurify: SAFE_FOR_TEMPLATES bypass - template expressions survive sanitization inside content when using DOM output modes

lowCVE-2026-65900
Summary When DOMPurify is configured with both SAFE_FOR_TEMPLATES: true and RETURN_DOM: true (or IN_PLACE: true), an attacker can inject template expressions, such as ${evil}, {{evil}}, or <%evil%>, that survive the sanitization pass inside <template> element content. This bypasses the explicit purpose of SAFE_FOR_TEMPLATES, which is to prevent template engine evaluation of user-supplied content. Note: The string output path is not affected. Only the DOM return paths (RETURN_DOM: true, RETURN_DOM_FRAGMENT: true, IN_PLACE: true) are vulnerable. Description Background SAFE_FOR_TEMPLATES is designed to strip {{ }}, ${ }, and <% %> expressions from sanitized output so that downstream template engines do not evaluate user-controlled content. The feature operates through two mechanisms: 1. Per-node scrubbing (_sanitizeElements, src/purify.ts:1403), scrubs individual text nodes during the main sanitization walk. 2. Final normalization pass (_scrubTemplateExpressions, src/purify.ts:1115), calls node.normalize() to merge adjacent text nodes, then walks the merged nodes and strips any expressions that only appeared after merging. The Gap _scrubTemplateExpressions uses a standard NodeIterator rooted at the output body: // src/purify.ts:1117 const walker = createNodeIterator.call( node.ownerDocument || node, node, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_COMMENT | ..., null ); Per the DOM specification, a NodeIterator does not descend into <template>.content. The template element's content is a separate DocumentFragment that lives outside the normal child-node tree. For the same reason, node.normalize() (called on line 1116) also does not normalize text nodes inside <template>.content. This means the final normalization and scrub pass, the only pass that catches expressions formed *by merging split text nodes*, never runs on <template> content. How Split Text Nodes Are Created When DOMPurify removes a disallowed element with KEEP_CONTENT: true (the default), it moves

Details

Source
GitHub Security Advisories (INTL · database · site)
Severity
low
Published
2026-06-15
Last updated
2026-07-23
Exploitation
Not in CISA KEV at last sync

Original advisory: https://github.com/advisories/GHSA-gvmj-g25r-r7wr

Exploitation outlook

EPSS (FIRST.org) estimates each CVE’s probability of exploitation in the next 30 days — here is the CSIRTS.com read on those numbers.

Referenced CVEs

CVECSIRTS overviewExternal
CVE-2026-65900coverage & exploitation statusNVD · CVE.org

Same CVEs, other sources

How other CERTs, PSIRTs and databases cover the vulnerabilities in this advisory.

More from GitHub Security Advisories