Medium

ApostropheCMS: Mutation-XSS / allowedTags bypass via literal `</textarea/>` solidus close

  • Disclosed: 03.09.2026
  • Updated: 11.10.2026 10:22

Summary

A mutation-XSS / allowedTags bypass: when textarea (or xmp) is included in allowedTags, an input containing a literal </textarea/> (a solidus right after the RCDATA end-tag name) lets non-allowed markup such as <img src=x onerror=…> 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.

Details

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 </textarea/> (solidus after the RCDATA end-tag name) as a close tag; it emits </textarea/><img …> 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 </textarea/> as a valid textarea close, so the following <img onerror> 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.

PoC

js
// npm i sanitize-html@2.17.5 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 <img src=x onerror=alert(document.domain)> at body level (it escaped the textarea RCDATA, not inert text) → the onerror fires in a browser. Confirmed on 2.17.5 (Node v24). A canonical poc.js is attached. <img width="650" height="118" alt="image" src="https://github.com/user-attachments/assets/d63bb5b7-ba3e-4b0e-a821-86a453ea0352" />

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; the maintainers have fixed this class before (e.g. GHSA-9mrh). 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). Suggested fix: route textarea/xmp content through escapeHtml instead of the raw passthrough, and/or fix the htmlparser2 </tag/> RCDATA end-tag tokenization to match the WHATWG spec.

Affected software

Click a technology to see all vulnerabilities affecting it.

PlatformPackageVulnerable versionsFixed in
npm package sanitize-html < 2.17.6 2.17.6

References

CVSS score 6.1/10
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Weakness (CWE)
CWE-79
Platform
npm
Package
sanitize-html
Disclosed
03.09.2026
Updated
11.10.2026 10:22

Sources and licenses

OSV / GitHub Advisory Database - GHSA-jxwj-j7wr-gfrw - CC BY 4.0

Vulnerability history: sanitize-html

All published vulnerabilities of this package (npm package), newest first. Shows whether this is a one-off slip or a package that needs watching.

All (15) in the archive
Total entries
15
First → latest
11.2018 10.2026
Without a fix
0
Exploited in attacks
0

Severity breakdown

  • Critical 1
  • High 1
  • Medium 12
  • Low 1

Disclosures per year

  1. 2018: 2
  2. 2019: 0
  3. 2020: 1
  4. 2021: 2
  5. 2022: 1
  6. 2023: 0
  7. 2024: 1
  8. 2025: 1
  9. 2026: 7
Severity:
  1. ApostropheCMS: Mutation-XSS / allowedTags bypass via literal `</textarea/>` solidus close this entry
    Medium · 6.1 CVE-2026-63670 < 2.17.6 2.17.6
  2. Critical · 9.3 CVE-2026-44990 >= 2.17.3, < 2.17.4 2.17.4
  3. High · 7.5 CVE-2022-25887 < 2.7.1 2.7.1

Know which vulnerabilities affect your application

With the DockRay integration and software inventory enabled, DockRay checks reported component versions against known vulnerabilities and notifies you about confirmed matches. Coverage depends on the supported technology and available advisory data.

Connect your application
DockRay newsletter

Application security in your inbox

Important vulnerabilities, actionable guidance and practical advice for your applications. Choose your technologies and follow what matters to your project.

  • A daily digest of new vulnerabilities, critical ones included.
  • Notifications about important updates.
  • Practical advice on application security and monitoring.

Leave empty to receive updates for every technology.

Free to subscribe. Unsubscribe with one click in any email.