CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-53606

CVE-2026-53606: Stored Cross-Site Scripting (XSS) via Unsanitized URI-bearing Attributes in sanitize-html

Alon Barad
Alon Barad
Software Engineer

Aug 1, 2026·6 min read·19 visits

Executive Summary (TL;DR)

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.

Vulnerability Overview

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.

Root Cause Analysis

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.

Code Analysis

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 & Attack Scenarios

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.

Impact Assessment

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.

Remediation & Fix Completeness

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.

Official Patches

ApostropheCMSOfficial Release Release Tag
ApostropheCMSOfficial Fix Commit

Fix Analysis (1)

Technical Appendix

CVSS Score
5.4/ 10
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:N
EPSS Probability
0.14%
Top 97% most exploited

Affected Systems

Apostrophe CMS configurations running vulnerable dependenciesAny Node.js applications using sanitize-html with custom allowed tags and attributes configurations prior to 2.17.5

Affected Versions Detail

Product
Affected Versions
Fixed Version
sanitize-html
ApostropheCMS
< 2.17.52.17.5
AttributeDetail
CWE IDCWE-79
Attack VectorNetwork
CVSS SeverityMedium (5.4)
EPSS Score0.00136
ImpactStored Cross-Site Scripting (XSS)
Exploit StatusProof of Concept
KEV StatusNot Listed

MITRE ATT&CK Mapping

T1189Drive-by Compromise
Initial Access
T1185Browser Session Hijacking
Collection
CWE-79
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

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.

Known Exploits & Detection

GitHub Security AdvisoryExploit concepts demonstrating action, formaction, data, and xlink:href bypass strings

Vulnerability Timeline

Fix patch was developed and committed by the primary ApostropheCMS project maintainer
2026-06-10
Official Security Advisory was published via GitHub Security Advisories
2026-06-12
Vulnerability registered and published on CVE.org as CVE-2026-53606
2026-06-12
Vulnerability recorded and updated on the NIST National Vulnerability Database (NVD)
2026-06-17

References & Sources

  • [1]GitHub Security Advisory
  • [2]Downstream Pull Request
  • [3]CVE.org Record
  • [4]NVD Directory Profile
  • [5]OSV Source JSON Schema

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•7 minutes ago•GHSA-4HV6-XC92-J86G
6.5

GHSA-4HV6-XC92-J86G: Insufficient Session Expiration in Vikunja WebSocket Authentication Pipeline

Vikunja versions 2.3.0 through 2.6.0 contain an insufficient session expiration vulnerability (CWE-613) within the WebSocket authentication handler. Although Vikunja enforces server-side session tracking and revocation for REST API routes, the WebSocket handshake handler validates cryptographic JWT signatures without querying the database session state. Consequently, revoked JWT tokens can establish new real-time WebSocket connections, and existing connections persist after session revocation.

Alon Barad
Alon Barad
0 views•5 min read
•about 1 hour ago•GHSA-FPRF-R6RV-XG99
6.5

GHSA-FPRF-R6RV-XG99: Cross-Tenant Task Position Recalculation in Vikunja

A cross-tenant boundary breach vulnerability in Vikunja allows an authenticated user to trigger global task position recalculations across all tenant instances by creating a saved filter with an empty filter string payload.

Amit Schendel
Amit Schendel
5 views•4 min read
•about 2 hours ago•GHSA-HJX8-QV73-F7CM
6.5

GHSA-HJX8-QV73-F7CM: Incomplete Access Revocation Leading to Webhook Data Exfiltration in Vikunja

An access revocation flaw in Vikunja allows removed collaborators to retain outbound webhooks and link shares created prior to revocation, enabling persistent exfiltration of sensitive task data.

Alon Barad
Alon Barad
5 views•5 min read
•about 3 hours ago•GHSA-PJR3-86V4-5P7W
5.4

GHSA-PJR3-86V4-5P7W: Broken Access Control via MAX Aggregation in Vikunja Subtree Permissions

Vikunja v2.6.0 contains a permission inheritance regression in pkg/models/project_access.go where explicit down-restrictions on sub-projects are overridden by higher parent project permissions due to MAX aggregation across project tree nodes.

Alon Barad
Alon Barad
5 views•6 min read
•about 4 hours ago•GHSA-FMMF-XQ98-G327
5.3

GHSA-fmmf-xq98-g327: Write-Level Project Members Can Delete Admin-Tier Link Shares in Vikunja

An authorization bypass vulnerability in Vikunja's link share deletion handlers allows project members with Write privileges to delete Admin-tier link shares. The handler passes an unpopulated struct to the authorization check, causing the permission evaluation to fall back to default Write permissions instead of requiring Admin privileges.

Alon Barad
Alon Barad
8 views•5 min read
•about 5 hours ago•GHSA-M687-P538-R5HP
7.2

GHSA-m687-p538-r5hp: Permissive Localhost CORS Policy Leads to Account Takeover in Vikunja

Vikunja versions 2.2.0 through 2.6.0 contain a Cross-Origin Resource Sharing (CORS) misconfiguration flaw in `code.vikunja.io/api`. Default configurations permit wildcard origins for localhost (`http://127.0.0.1:*` and `http://localhost:*`) with credentialed requests (`Access-Control-Allow-Credentials: true`). Because configuring a public service URL appends to this default list rather than overriding it, production environments inadvertently trust all local origins. A local page or application on a user's machine can execute a credentialed cross-origin request to the token refresh endpoint, extract the returned JWT access token, and achieve complete account takeover.

Alon Barad
Alon Barad
8 views•5 min read