Cloudanix VulnDB

CVE-2026-65900

Medium · 6.1

CVSS

6.1

EPSS

0.3%

Published

Jul 23, 2026

CISA due date

—

Cloudanix analysis

Next patch cycle SSVC: Track

A website feature that cleans user-submitted HTML may miss hidden template code, allowing malicious content to run when someone views it.

Chainable
Exploitation
Requires User Interaction
Attacker needs
None
Complexity
Low
Impact
Data Tampering
Component
Library
Class
Improper Input Validation
Remediation
Upgrade
Effort
Medium
Patch risk
Low

What it actually does

DOMPurify 3.0.0 through versions before 3.4.8 can fail to remove template expressions when SAFE_FOR_TEMPLATES is used together with a DOM output mode such as RETURN_DOM, RETURN_DOM_FRAGMENT, or IN_PLACE. The flaw occurs because the final template-expression scrub pass does not inspect the content of HTML <template> elements after adjacent text nodes are normalized, allowing expressions such as ${...}, {{...}}, or <%...%> to remain in sanitized DOM output. If that sanitized output is later processed by a client-side or server-side template engine, attacker-controlled expressions may be evaluated, potentially causing cross-site scripting, template injection, data exposure within the template context, or unauthorized actions performed in a victim user's session. The string output path is not affected.

Why it matters to the business

Applications that rely on DOMPurify to make user-supplied HTML safe may incorrectly trust malicious content when using the vulnerable configuration. The main business risk is compromise of users interacting with attacker-supplied content, including session abuse, unauthorized actions, content manipulation, or exposure of information available to the affected page or template context. The issue is not currently listed in CISA KEV, has low EPSS, and has no reported exploit maturity in the supplied data, but it is easy to trigger where the vulnerable configuration exists and should be remediated during normal patch cycles, faster for applications that render untrusted HTML into templates.

How an attack unfolds

An attacker first finds an application feature that accepts or stores HTML-like input and sanitizes it with DOMPurify configured with SAFE_FOR_TEMPLATES plus a DOM output mode. The attacker submits markup containing a <template> element with deliberately split text nodes that normalize into a template expression, such as a mustache, JavaScript template literal, or ERB-style expression. Because the vulnerable scrub logic does not descend into template.content after normalization, the expression survives sanitization. The application then inserts or passes the sanitized DOM into a downstream template engine or rendering layer. When a victim views the content, that downstream engine may evaluate the attacker-controlled expression, leading to script execution or template-context abuse depending on the framework and privileges of the viewer.

How to fix it

Upgrade DOMPurify to version 3.4.8 or later wherever DOMPurify 3.x is used. Review package lockfiles and bundled frontend assets to ensure the runtime version actually loaded by browsers or server-side rendering code is fixed. If immediate upgrade is not possible, avoid using SAFE_FOR_TEMPLATES with RETURN_DOM, RETURN_DOM_FRAGMENT, or IN_PLACE for untrusted content; prefer the unaffected string output path and re-parse only in a safe context if necessary. As defense in depth, avoid passing sanitized user content into a template engine, disable template evaluation for user-controlled content, and enforce a restrictive Content Security Policy to reduce impact if script execution is achieved.

How to confirm the fix worked

Confirm dependency manifests and lockfiles resolve DOMPurify to 3.4.8 or later, and verify built artifacts do not include an older bundled copy. Search application code for DOMPurify usage combining SAFE_FOR_TEMPLATES with RETURN_DOM, RETURN_DOM_FRAGMENT, or IN_PLACE. Add a regression test that sanitizes a payload containing a <template> element with split template-expression text nodes and verifies expressions such as ${...}, {{...}}, and <%...%> are removed from template.content. For runtime validation, test representative user-content rendering paths to ensure sanitized content is not subsequently evaluated by a template engine.

Typical attack path

  1. Identify an application feature that accepts untrusted HTML or rich text input.
  2. Confirm the application uses DOMPurify with SAFE_FOR_TEMPLATES and a DOM return mode such as RETURN_DOM, RETURN_DOM_FRAGMENT, or IN_PLACE.
  3. Craft HTML containing a <template> element with template-expression syntax split across adjacent text nodes so the dangerous expression forms after DOM normalization.
  4. Submit or store the crafted content in the application.
  5. Wait for the application to sanitize the content and then pass the DOM output to a downstream template or rendering engine.
  6. Trigger rendering by a victim user or privileged workflow.
  7. Abuse evaluated template expressions to execute script, access template-context data, or perform actions available to the victim session.

How to detect it

To determine whether exploitation already occurred, review application logs, WAF logs, reverse-proxy logs, request bodies, stored-content audit trails, and moderation histories for user-submitted HTML containing <template> elements with template-expression syntax. Focus on endpoints that create or update rich text, comments, messages, profiles, CMS pages, documentation, email templates, or other stored HTML. Search source and build artifacts for vulnerable DOMPurify usage patterns to identify exposed paths, then review content submitted to those paths. In browser telemetry or CSP violation reports, look for script execution, blocked inline scripts, unusual outbound requests, or DOM-based errors occurring after rendering user-generated content. If downstream server-side templates are involved, inspect application error logs for template parsing or evaluation errors involving user-controlled content. Because no public exploit indicators or named threat actors are provided, detection should be behavior- and payload-focused rather than actor-specific.

  • SAFE_FOR_TEMPLATES
  • RETURN_DOM
  • RETURN_DOM_FRAGMENT
  • IN_PLACE
  • <template
  • </template>
  • ${
  • {{
  • }}
  • <%
  • %>
  • DOMPurify.sanitize
  • node_modules/dompurify
  • dompurify@3.0.0
  • dompurify@3.1.
  • dompurify@3.2.
  • dompurify@3.3.
  • dompurify@3.4.0
  • dompurify@3.4.1
  • dompurify@3.4.2
  • dompurify@3.4.3
  • dompurify@3.4.4
  • dompurify@3.4.5
  • dompurify@3.4.6
  • dompurify@3.4.7

What your security team should do

  • Inventory all direct and transitive DOMPurify usage, including frontend bundles, server-side rendering code, CMS plugins, and vendored JavaScript libraries.
  • Upgrade DOMPurify to 3.4.8 or later and redeploy affected assets; ensure caches and CDNs no longer serve vulnerable bundles.
  • Prioritize applications that accept untrusted rich text, comments, profile content, messages, documentation, or CMS content and later render it through a template engine.
  • Search code for SAFE_FOR_TEMPLATES combined with RETURN_DOM, RETURN_DOM_FRAGMENT, or IN_PLACE and refactor risky flows to avoid DOM output modes for untrusted content.
  • Add automated dependency scanning and build-time checks to block DOMPurify versions >=3.0.0 and <3.4.8.
  • Review stored user-generated content for suspicious <template> payloads and template-expression syntax, especially in content created shortly before patching.
  • Deploy or tighten Content Security Policy controls to limit inline script execution and reduce exploitation impact.
  • Monitor web, application, and WAF logs for submitted HTML containing <template> elements combined with ${...}, {{...}}, or <%...%> expression syntax.

The record

DOMPurify versions >=3.0.0 and before 3.4.8, when configured with SAFE_FOR_TEMPLATES together with a DOM output mode (RETURN_DOM, RETURN_DOM_FRAGMENT, or IN_PLACE), fail to strip template expressions (e.g. ${evil}, {{evil}}, <%evil%>) inside <template> element content. The final normalization/scrub pass (_scrubTemplateExpressions) uses a NodeIterator and node.normalize() that do not descend into template.content, so expressions that only form after adjacent text nodes merge survive sanitization. This bypasses SAFE_FOR_TEMPLATES and can allow a downstream template engine to evaluate attacker-supplied expressions. The string output path is not affected.

Affected packages

Ecosystem Package Introduced Fixed

Related vulnerabilities