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

Published Aug 17, 2026
·
Updated

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.

Other sources

ApostropheCMS is an open-source Node.js content management system. Prior to 2.17.6, sanitizeHtml() can pass disallowed executable markup through packages/sanitize-html/index.js when textarea or xmp is included in allowedTags because a literal solidus after the raw-text end-tag name is treated as text by htmlparser2 and the ontext handler emits that content without escaping, while a browser parses the following img onerror markup as active HTML. This issue is fixed in version 2.17.6.

— NVD

Affected Software

2 affected componentsFixes available
npm/apostrophe<2.17.6
npm/sanitize-html<=2.17.5
2.17.6

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade npm/sanitize-html to a version that resolves this vulnerability.

    Fixed in 2.17.6
  2. Upgrade

    Upgrade sanitize-html to a version that resolves this vulnerability.

    Fixed in 2.17.6Patch GHSA-9mrh
  3. Configuration

    To avoid the literal-solidus RCDATA end-tag mis-tokenization (e.g., `</textarea/>`), configure sanitize-html so `allowedTags` does not add `textarea`/`xmp`. The issue occurs when `textarea` (or `xmp`) is present in `allowedTags`.

    sanitize-html allowedTags = do not include 'textarea' or 'xmp' unless required

Event History

Aug 17, 2026
CVE Published
via MITRE·07:49 PM
Data Sourced
via MITRE·07:49 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·08:16 PM
DescriptionSeverityWeakness
Sep 3, 2026
Advisory Published
via GitHub·08:07 PM
Data Sourced
via GitHub·08:07 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

What is the risk level of CVE-2026-63670?

The risk level of CVE-2026-63670 is categorized as medium with a severity score of 6.1.

2

How can I fix CVE-2026-63670?

To fix CVE-2026-63670, update ApostropheCMS to version 2.17.6 or later.

3

What type of vulnerability is CVE-2026-63670?

CVE-2026-63670 is classified as a Mutation-XSS vulnerability.

4

What components of ApostropheCMS are affected by CVE-2026-63670?

CVE-2026-63670 affects the sanitizeHtml() functionality in ApostropheCMS prior to version 2.17.6.

5

Is user interaction required for CVE-2026-63670 to be exploited?

Yes, user interaction is required for CVE-2026-63670 to be exploited, as noted in its characteristics.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203