Aug 1, 2026·6 min read·1 visit
The sanitize-html library bypassed URI scheme validation for standard attributes such as 'action' or 'formaction' when customized configurations allowed them, enabling Stored Cross-Site Scripting.
An incomplete default configuration vulnerability in sanitize-html prior to version 2.17.5 allows remote attackers to execute arbitrary JavaScript code via crafted HTML payloads containing neglected URI-bearing attributes (e.g., action, formaction, data, xlink:href) that bypass input validation logic.
The Node.js library sanitize-html is widely utilized to parse and clean untrusted HTML inputs, stripping hazardous tags, attributes, and malicious URI schemes before rendering. Applications use this package to permit safe rich-text formatting while mitigating browser exploitation vectors. The security model relies on strict control over allowed elements and the evaluation of URIs inside specific attributes.
Prior to version 2.17.5, sanitize-html contained a default configuration gap that restricted URI protocol filtering to a small default subset of attributes. If developers customized their allowed elements list to include other standard HTML5 elements with URI-bearing properties, the library failed to strip harmful schemes like javascript: or vbscript:. This architectural gap resulted in a Stored Cross-Site Scripting (XSS) vulnerability tracked as CVE-2026-53606.
The vulnerability is classified under CWE-79: Improper Neutralization of Input During Web Page Generation. The lack of standard validation for alternate URI-bearing attributes allowed malicious payloads to survive parsing and persist in the application database, executing arbitrary JavaScript in the victim's session upon subsequent page render.
The vulnerability stems from the implementation of the allowedSchemesAppliedToAttributes configuration property. To detect and strip hazardous URI schemes, sanitize-html employs an internal safety check called naughtyHref(). This function parses the target URI and discards the attribute value if it contains unauthorized protocols such as javascript:, data:, or vbscript:.
To balance execution performance and avoid corrupting non-URI string properties, the library does not pass every single HTML attribute through the naughtyHref() engine. Instead, it references allowedSchemesAppliedToAttributes as a gatekeeper list. Prior to version 2.17.5, this list was hardcoded by default to only contain three attributes: href, src, and cite.
When a developer customized the sanitizer configuration to allow tags and attributes beyond the default safe-list, the parser analyzed the configured elements. If an allowed attribute was not explicitly included in the default allowedSchemesAppliedToAttributes array, the sanitizer skipped protocol validation. Attributes like action on <form>, formaction on <button>, or data on <object> bypassed security verification entirely.
To understand the code-level flaw, we must examine the validation loop inside packages/sanitize-html/index.js. The parser loops through attributes and checks whether they should be validated based on their presence in the allowedSchemesAppliedToAttributes array.
In the vulnerable implementation, the default options block was defined as follows:
// VULNERABLE CODE (Prior to 2.17.5)
sanitizeHtml.defaults = {
allowedSchemes: [ 'http', 'https', 'ftp', 'mailto', 'tel' ],
allowedSchemesByTag: {},
allowedSchemesAppliedToAttributes: [ 'href', 'src', 'cite' ], // Highly restricted list
allowProtocolRelative: true,
enforceHtmlBoundary: false
};When the library evaluated an attribute like formaction, it checked whether the name was present in the allowedSchemesAppliedToAttributes array. Because formaction was missing, the logic skipped the naughtyHref validation path. The patched version expands this array to cover all HTML5 specifications where a browser would parse and execute a URI:
// PATCHED CODE (Version 2.17.5)
sanitizeHtml.defaults = {
allowedSchemes: [ 'http', 'https', 'ftp', 'mailto', 'tel' ],
allowedSchemesByTag: {},
allowedSchemesAppliedToAttributes: [
'href', 'src', 'cite',
'action', 'formaction', 'data', 'xlink:href',
'poster', 'background', 'ping',
'longdesc', 'usemap', 'codebase', 'classid', 'archive',
'profile', 'manifest', 'itemid',
'dynsrc', 'lowsrc'
],
allowProtocolRelative: true,
enforceHtmlBoundary: false
};Additionally, a flaw in the srcset parser logic was corrected. The original parser evaluated imagesrcset values using the string literal 'srcset' in the inner loop, preventing correct validation of imagesrcset schemes. The patch resolved this by passing the variable attribute name a dynamically to naughtyHref(a, value.url) rather than a hardcoded string.
Exploitation of CVE-2026-53606 is straightforward and relies on typical input vectors where standard attributes are allowed by the target application configuration. For instance, if an application supports custom form generation and allows users to define <form> or <button> elements, an attacker can input an executable javascript payload within the form's target action.
An attacker crafts a payload utilizing the <form> tag and the action attribute. When parsed by vulnerable versions of sanitize-html configured to allow <form> and action, the input <form action="javascript:alert(document.cookie)"> remains unmodified. When a user interacts with the form or submits it, the browser parses the javascript: pseudo-protocol and executes the trailing payload within the context of the vulnerable origin.
Another highly effective vector targets the <object> element using the data attribute. If the sanitizer configuration allows the <object> tag with the data attribute, an attacker can supply <object data="javascript:alert(document.domain)"></object>. The browser automatically parses and executes the data property upon loading the element in the DOM, executing the script with no user interaction required.
The security impact of CVE-2026-53606 is characterized by Stored Cross-Site Scripting (XSS). Because the input parsing is executed server-side prior to database entry, malicious payloads are stored statically. Any user requesting the associated page or rendering the sanitized output executes the injected JavaScript code immediately in their browser context.
An attacker can exploit this flaw to execute arbitrary client-side code, hijack user sessions, steal session identifiers stored in non-HttpOnly cookies, and capture local storage tokens. In a scenario involving administrator victims, this execution path can lead to administrative panel compromise, configuration modification, or full application takeover.
The vulnerability is assessed with a CVSS v3.1 score of 5.4. While the attack complexity is low and exploitation is straightforward, it requires some user interaction and relies on custom configurations where specific attributes are allowed. The scope is changed because execution bypasses the sandbox constraints of the HTML parser and impacts the broader browser context.
The primary remediation for this vulnerability is upgrading sanitize-html to version 2.17.5 or above. This release updates the default allowedSchemesAppliedToAttributes array to include all standard HTML5 URI-accepting attributes, mitigating the vector without requiring manual configuration adjustments.
If immediate dependency updates are not possible due to deployment freezes, a manual configuration override serves as an effective hotfix. Developers must explicitly define the expanded allowedSchemesAppliedToAttributes array when initializing the sanitizer in their application code, replicating the updated list of 20 attributes introduced in the official patch.
// Remediation Hotfix Workaround
const sanitizeHtml = require('sanitize-html');
const cleanHtml = sanitizeHtml(dirtyInput, {
allowedSchemesAppliedToAttributes: [
'href', 'src', 'cite',
'action', 'formaction', 'data', 'xlink:href',
'poster', 'background', 'ping',
'longdesc', 'usemap', 'codebase', 'classid', 'archive',
'profile', 'manifest', 'itemid',
'dynsrc', 'lowsrc'
]
});The fix is complete regarding standard attributes defined in modern browser execution environments. However, applications should also deploy a robust Content Security Policy (CSP) to restrict inline script execution and limit connections to trusted domains, providing defense-in-depth protection against parsing or sanitization bypasses.
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
sanitize-html ApostropheCMS | < 2.17.5 | 2.17.5 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-79 |
| Attack Vector | Network |
| CVSS Severity | Medium (5.4) |
| EPSS Score | 0.00136 |
| Impact | Stored Cross-Site Scripting (XSS) |
| Exploit Status | Proof of Concept |
| KEV Status | Not Listed |
The software does not neutralize or incorrectly neutralizes user-controlled input before it is placed in output that is used as a web page that is served to other users.
An authenticated file upload validation bypass vulnerability exists in the REDAXO CMS Mediapool addon in versions 5.18.2 through 5.21.0. Under permissive web server configurations, this allows authenticated users with media upload privileges to achieve remote code execution via multi-segment extension file uploads.
A critical SQL injection vulnerability exists in the @nocobase/plugin-notification-in-app-message plugin of NocoBase prior to version 2.0.61. The flaw is caused by direct string interpolation of user-controlled input into a Sequelize.literal() query, allowing authenticated users to execute stacked PostgreSQL queries and achieve remote code execution on the underlying database server.
A critical server-side prototype pollution vulnerability in ApostropheCMS versions up to and including 4.30.0 allows authenticated editors to write arbitrary properties to the global Object.prototype via patch operators. Exploiting a confirmed gadget in publicApiCheck() bypasses authorization on all piece-type REST API endpoints framework-wide, persisting for the lifetime of the Node.js process.
An unauthenticated Server-Side Request Forgery (SSRF) vulnerability exists in ApostropheCMS versions up to and including 4.30.0. When the prettyUrls option is enabled in the @apostrophecms/file module, the server constructs internal self-requests using the client-provided HTTP Host header, allowing remote attackers to coerce the server into initiating outbound requests to arbitrary internal or external hosts.
A stored Cross-Site Scripting (XSS) vulnerability exists in the @apostrophecms/seo package of the ApostropheCMS ecosystem up to and including version 1.4.2. Unsanitized user inputs for Google Analytics and Google Tag Manager IDs are injected directly into script elements within the document header, enabling authenticated editors to execute arbitrary JavaScript in the context of all site visitors.
A remote denial of service vulnerability exists in the pion/stun package before version 3.1.3. A malformed STUN packet containing a short or empty XOR-MAPPED-ADDRESS attribute triggers a runtime slice-bounds panic during parsing, terminating the entire Go process.