Listen to this Post
A mutation-XSS / allowedTags bypass occurs when `textarea` (or xmp) is included in allowedTags. An input containing a literal `` (a solidus right after the RCDATA end-tag name) lets non-allowed markup such as `` pass through `sanitizeHtml()` live and unescaped, even though
img/onerror are not in the allowlist. A spec-compliant browser executes the surviving handler — XSS.
This is a literal-solidus variant that bypasses the two most recent fixes in this code area (CVE-2026-40186, CVE-2026-44990), both already applied in 2.17.5. The default configuration is not affected.
`sanitize-html` emits the text content of HTML raw-text elements (textarea, xmp) without escaping. Two things combine:
– Parser differential: on input, `htmlparser2` does NOT recognize `` (solidus after the RCDATA end-tag name) as a close tag; it emits `` as a single raw-text node.
– Unescaped passthrough: the `ontext` handler (index.js ~575-583) appends textarea/xmp content with `result += text` (no escapeHtml), assuming it is “already properly encoded” — true for entity-decoded content (what CVE-2026-40186 fixed) but false for this mis-tokenized literal close tag.
A spec browser treats `` as a valid `textarea` close, so the following `` is parsed as a live element. The recent fixes addressed entity-encoding (CVE-2026-40186) and the `xmp` default (CVE-2026-44990); neither covers the literal-solidus mis-tokenization, so the raw passthrough still leaks.
DailyCVE Form:
Platform: Node.js
Version: 2.17.5
Vulnerability: XSS
Severity: Medium
date: 2026-09-03
Prediction: 2026-09-10
What Undercode Say:
npm i [email protected] parse5 && node poc.js const sanitizeHtml = require('sanitize-html'); const input = '<textarea></textarea/><img src=x onerror="alert(document.domain)">'; const opts = { allowedTags: sanitizeHtml.defaults.allowedTags.concat(['textarea']) }; // img NOT allowed console.log(sanitizeHtml(input, opts)); // => <textarea></textarea/><img src=x onerror="alert(document.domain)"></textarea> // the <img onerror> survives live and unescaped console.log(sanitizeHtml(input)); // default config (no textarea allowed) => "" (safe)
Re-parsing the sanitized OUTPUT with `parse5` (the WHATWG HTML parser browsers/jsdom use) yields a live `` at body level (it escaped the textarea RCDATA, not inert text) → the `onerror` fires in a browser. Confirmed on 2.17.5 (Node v24).
Exploit: (Educational Purposes!)
- Identify a target application using `sanitize-html` version 2.17.5 or earlier with `textarea` (or
xmp) inallowedTags.
2. Craft a payload: `
`
- Submit the payload through any user-input field that is processed by the vulnerable sanitizer.
- When the sanitized output is rendered in a victim’s browser, the `onerror` event handler executes, leading to XSS.
Protection:
- Upgrade to `sanitize-html` version 2.17.6 or later, once a patch is released.
- As a temporary workaround, do not include `textarea` or `xmp` in the `allowedTags` configuration. The default configuration is not affected.
- Apply a Content Security Policy (CSP) to mitigate the impact of any potential XSS.
Impact:
Cross-site scripting (CWE-79). Requires `textarea` (or xmp) in `allowedTags` — a benign-looking, common addition in form builders, CMS, and rich-text editors. Adding a harmless tag that then enables XSS via non-allowed img/onerror breaks the sanitizer’s core contract.
An attacker who can submit content rendered through such a configuration achieves stored/reflected XSS (cookie theft, session hijack). Severity Medium (default config is safe; user interaction to view the page).
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

