Skip to main content

sanitize-html EUVDEUVD-2026-36566

| CVE-2026-44990 CRITICAL
Cross-site Scripting (XSS) (CWE-79)
2026-05-14 https://github.com/apostrophecms/apostrophe GHSA-rpr9-rxv7-x643
9.3
CVSS 3.1 · Vendor: https://github.com/apostrophecms/apostrophe
Share

Severity by source

Vendor (https://github.com/apostrophecms/apostrophe) PRIMARY
9.3 CRITICAL
AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N
SUSE
CRITICAL
qualitative
Red Hat
8.1 HIGH
qualitative

Primary rating from Vendor (https://github.com/apostrophecms/apostrophe).

CVSS VectorVendor: https://github.com/apostrophecms/apostrophe

Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
Required
Scope
Changed
Confidentiality
High
Integrity
High
Availability
None

Lifecycle Timeline

3
Source Code Evidence Fetched
May 14, 2026 - 19:01 vuln.today
Analysis Generated
May 14, 2026 - 19:01 vuln.today
CVE Published
May 14, 2026 - 18:26 nvd
CRITICAL 9.3

Blast Radius

ecosystem impact
† from your stack dependencies † transitive graph · vuln.today resolves 4-path depth
  • 55 npm packages depend on sanitize-html (13 direct, 42 indirect)

Ecosystem-wide dependent count for version 2.17.3.

DescriptionCVE.org

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
  1. 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>'));
  1. Observed output:
html
<script>alert(1)</script>
<img src=x onerror=alert(1)>
<svg><script>alert(1)</script></svg>
  1. 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.

AnalysisAI

The sanitize-html npm package allows remote attackers to bypass HTML sanitization and inject executable JavaScript by wrapping malicious payloads inside disallowed <xmp> tags, achieving stored cross-site scripting (XSS) in applications using default configurations. This affects all versions through 2.17.3, with no vendor-released patch identified at time of analysis. A publicly available proof-of-concept demonstrates the bypass, which leverages the library's special handling of raw-text elements. With a 9.3 CVSS score and network-based attack vector requiring only user interaction, this represents a critical risk for Node.js applications that render sanitized user content in browsers.

Technical ContextAI

The vulnerability stems from an incomplete implementation of HTML5 raw-text element handling in sanitize-html's parser integration with htmlparser2. The library's default nonTextTags list (index.js lines 138-142) includes only script, style, textarea, and option tags for complete content dropping, but omits the xmp element. However, the ontext handler (lines 569-577) explicitly special-cases both textarea and xmp, appending their text content directly to output without HTML entity encoding. Because htmlparser2 correctly treats xmp as a raw-text element per HTML5 parsing rules, markup inside xmp is parsed as text nodes during sanitization but becomes live executable HTML when the unescaped text is reassembled in the output string. This creates a context-switching bypass where disallowed tags like script can be smuggled through sanitization wrapped in an outer xmp container that itself gets discarded while its 'text' payload survives unescaped. The vulnerability is a classic double-parsing or content-type mismatch issue rooted in CWE-79 (Improper Neutralization of Input During Web Page Generation).

RemediationAI

No vendor-released patch identified at time of analysis for sanitize-html versions <= 2.17.3. Organizations should monitor the project's GitHub repository (https://github.com/apostrophecms/sanitize-html) and advisory GHSA-rpr9-rxv7-x643 for patch release announcements. As an immediate compensating control, add 'xmp' to the nonTextTags configuration array to force complete content dropping for xmp elements, preventing the unescaped text passthrough: sanitizeHtml(input, { nonTextTags: ['script', 'style', 'textarea', 'option', 'xmp'] }). This configuration change aligns xmp handling with the library's treatment of other raw-text elements and blocks the bypass vector. Trade-off: requires code changes to every sanitize-html invocation if using a wrapper function, or modification of application-wide sanitizer configuration. Alternative mitigation: implement Content Security Policy (CSP) with strict-dynamic and nonce-based script whitelisting in the browser layer to prevent execution of injected inline scripts even if sanitization is bypassed; however, this does not prevent DOM-based XSS via event handlers like onerror, so CSP alone is insufficient. For applications unable to patch immediately, consider replacing sanitize-html with DOMPurify (isomorphic-dompurify for Node.js) or implementing server-side rendering with context-aware output encoding.

CVE-2024-41713 CRITICAL POC
9.1 Oct 21

A vulnerability in the NuPoint Unified Messaging (NPM) component of Mitel MiCollab through 9.8 SP1 FP2 (9.8.1.201) could

CVE-2024-55591 CRITICAL POC
9.8 Jan 14

FortiOS and FortiProxy contain an authentication bypass via the Node.js websocket module allowing unauthenticated remote

CVE-2023-44487 HIGH POC
7.5 Oct 10

Denial of service against HTTP/2 server implementations allows remote unauthenticated attackers to exhaust server resour

CVE-2014-7205 CRITICAL POC
10.0 Oct 08

Eval injection vulnerability in the internals.batch function in lib/batch.js in the bassmaster plugin before 1.5.2 for t

CVE-2025-59528 CRITICAL POC
10.0 Sep 22

Flowise version 3.0.5 contains a remote code execution vulnerability in the CustomMCP node. The mcpServerConfig paramete

CVE-2017-14849 HIGH POC
7.5 Sep 28

Node.js 8.5.0 before 8.6.0 allows remote attackers to access unintended files, because a change to ".." handling was inc

CVE-2017-5941 CRITICAL POC
9.8 Feb 09

An issue was discovered in the node-serialize package 0.0.4 for Node.js. Rated critical severity (CVSS 9.8), this vulner

CVE-2014-0224 HIGH POC
7.4 Jun 05

OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph

CVE-2014-3744 HIGH POC
7.5 Oct 23

Directory traversal vulnerability in the st module before 0.2.5 for Node.js allows remote attackers to read arbitrary fi

CVE-2014-9566 HIGH POC
7.5 Mar 10

Multiple SQL injection vulnerabilities in the Manage Accounts page in the AccountManagement.asmx service in the Solarwin

CVE-2013-4660 MEDIUM POC
6.8 Jun 28

The JS-YAML module before 2.0.5 for Node.js parses input without properly considering the unsafe !!js/function tag, whic

CVE-2016-2107 MEDIUM POC
5.9 May 05

The AES-NI implementation in OpenSSL before 1.0.1t and 1.0.2 before 1.0.2h does not consider memory allocation during a

Vendor StatusVendor

SUSE

Severity: Critical
Product Status
openSUSE Tumbleweed Fixed
SUSE Linux Enterprise High Performance Computing 12 Not-Affected
SUSE Linux Enterprise High Performance Computing 15 SP7 Not-Affected
SUSE Linux Enterprise High Performance Computing 15 SP7 Not-Affected
SUSE Linux Enterprise Micro 5.3 Not-Affected

Share

EUVD-2026-36566 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy