CVE-2026-53606: sanitize-html has an incomplete URI scheme validation that allows javascript: URIs through action, formaction, data, poster, and background attributes

Published Jun 12, 2026
·
Updated

Summary

sanitize-html uses allowedSchemesAppliedToAttributes (default: ['href', 'src', 'cite']) to gate the naughtyHref() function that blocks dangerous URI schemes like javascript: and vbscript:. The HTML specification defines 10+ attributes that accept URIs (action, formaction, data, poster, background, ping, xlink:href, dynsrc, lowsrc), but none of these are included in the default gate list. When a developer allows any of these attributes in their configuration, javascript: URIs pass through completely unmodified, enabling XSS.

The library has zero awareness of these URI-bearing attributes — none appear anywhere in the 854-line source file (verified by grep). No warning mechanism exists, and the README provides no security guidance about expanding allowedSchemesAppliedToAttributes when allowing form or media attributes.

Severity

Exploitation requires non-default configuration: the developer must explicitly allow a non-default tag (e.g., form) AND a non-default attribute (e.g., action). Default configuration is NOT vulnerable. However, this is a common configuration pattern for CMS platforms, form builders, and rich content editors.

Affected Versions

All versions of sanitize-html from v1.18.0 (which introduced allowedSchemesAppliedToAttributes) through at least v2.17.2. The default list has been ['href', 'src', 'cite'] since introduction and has never been expanded.

Root Cause

File: index.js:329 (sanitize-html 2.10.0, confirmed same in 2.17.x)

javascript // Line 329 — The gate that controls scheme validation if (options.allowedSchemesAppliedToAttributes.indexOf(a) >= 0) { if (naughtyHref(name, value)) { delete frame.attribs[a]; return; } }

Default list at line 829: javascript allowedSchemesAppliedToAttributes: ['href', 'src', 'cite'],

The naughtyHref() function (lines 627-667) correctly blocks javascript:, vbscript:, and other dangerous schemes. However, it has exactly 2 call sites in the entire codebase (lines 330 and 395), both inside the indexOf gate. There is no ungated path.

When attribute name is action, formaction, data, poster, background, etc.: - indexOf('action') returns -1 - The if block is skipped entirely - naughtyHref() is never called - javascript:alert(1) passes through unmodified

The escapeHtml() function at line 464 provides no defense — it only encodes & < > " characters, which are not present in javascript:alert(1).

Data Flow: Attacker input: <form action="javascript:alert(document.cookie)"> 1. htmlparser2 parses → tag='form', attribs={action:'javascript:alert(document.cookie)'} 2. index.js:298 → allowedAttributes check: 'action' in developer config → PASS 3. index.js:329 → ['href','src','cite'].indexOf('action') → -1 → SKIP naughtyHref() 4. index.js:464 → escapeHtml('javascript:alert(document.cookie)') → unchanged 5. OUTPUT: <form action="javascript:alert(document.cookie)">

Steps to Reproduce

javascript const sanitize = require('sanitize-html');

// ===== VECTOR 1: form action (100% reliable, all modern browsers) ===== const v1 = sanitize( '<form action="javascript:alert(document.cookie)"><button>Submit</button></form>', { allowedTags: ['form', 'button'], allowedAttributes: { form: ['action'] } } ); console.log('V1 (action):', v1); // OUTPUT: <form action="javascript:alert(document.cookie)"><button>Submit</button></form> // XSS triggers when user submits the form

// ===== VECTOR 2: button formaction (100% reliable) ===== const v2 = sanitize( '<button formaction="javascript:alert(1)">Click</button>', { allowedTags: ['button'], allowedAttributes: { button: ['formaction'] } } ); console.log('V2 (formaction):', v2); // OUTPUT: <button formaction="javascript:alert(1)">Click</button>

// ===== VECTOR 3: object data ===== const v3 = sanitize( '<object data="javascript:alert(1)"></object>', { allowedTags: ['object'], allowedAttributes: { object: ['data'] } } ); console.log('V3 (data):', v3); // OUTPUT: <object data="javascript:alert(1)"></object>

// ===== CONTROL: href IS scheme-checked (expected behavior) ===== const ctrl = sanitize( '<a href="javascript:alert(1)">click</a>', { allowedTags: ['a'], allowedAttributes: { a: ['href'] } } ); console.log('Control (href):', ctrl); // OUTPUT: <a>click</a> ← href correctly stripped by naughtyHref()

Observed behavior: javascript: preserved on action/formaction/data but correctly stripped on href.

Expected behavior: javascript: should be stripped on ALL URI-bearing attributes, or at minimum, the library should warn developers when they allow URI-bearing attributes not covered by scheme validation.

Impact

An attacker can achieve XSS in applications that use sanitize-html with non-default configurations allowing URI-bearing attributes:

- <form action="javascript:..."> — XSS on form submission (all modern browsers) - <button formaction="javascript:..."> — per-button XSS override (all modern browsers) - <object data="javascript:..."> — object load XSS (Chrome, Firefox) - <video poster="javascript:..."> — limited browser support but spec-valid

Common vulnerable configurations: - CMS platforms allowing form elements for user-generated content - Form builder applications - Rich text editors with extended tag allowlists - Email template editors allowing media/embed tags

Mitigating factors: - Default configuration is NOT vulnerable - Requires double opt-in: non-default tag + non-default attribute - CSP form-action directive mitigates form-based vectors - Developers CAN manually add attributes to allowedSchemesAppliedToAttributes

Remediation

Option 1 (Recommended): Expand the default allowedSchemesAppliedToAttributes list:

javascript // index.js line 829, change from: allowedSchemesAppliedToAttributes: ['href', 'src', 'cite'],

// to: allowedSchemesAppliedToAttributes: [ 'href', 'src', 'cite', 'action', 'formaction', 'data', 'poster', 'background', 'ping', 'xlink:href', 'dynsrc', 'lowsrc' ],

Option 2: Apply naughtyHref() to ALL attributes by default (invert the gate logic).

Option 3: Add a runtime warning when developers allow URI-bearing attributes not in allowedSchemesAppliedToAttributes (analogous to vulnerableTags warning for script/style at lines 124-129).

Reporter

Kevin Lee (Changseon Lee) OPCIA Corp. / PeanutAI Inc. Seoul, South Korea GitHub: crattack

Other sources

ApostropheCMS is an open-source Node.js content management system, and sanitize-html provides a simple HTML sanitizer with a clear API. Versions of sanitize-html prior to 2.17.5 use allowedSchemesAppliedToAttributes (default: ['href', 'src', 'cite']) to gate the naughtyHref() function that blocks dangerous URI schemes like javascript: and vbscript:. The HTML specification defines 10+ attributes that accept URIs (action, formaction, data, poster, background, ping, xlink:href, dynsrc, lowsrc), but none of these are included in the default gate list. When a developer allows any of these attributes in their configuration, javascript: URIs pass through completely unmodified, enabling XSS. Version 2.17.5 patches the issue.

MITRE

Affected Software

2 affected componentsFixes available
npm/sanitize-html<2.17.5
npm/sanitize-html>=1.18.0<=2.17.4
2.17.5

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.5
  2. Upgrade

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

    Fixed in 2.17.5
  3. Configuration

    If you are on a sanitize-html version prior to 2.17.5, expand the default allowedSchemesAppliedToAttributes list so that any URI-bearing attributes you allow (e.g., action/formaction/data/poster/background/…) are included; otherwise javascript: URIs can pass unmodified because naughtyHref() is skipped for those attributes.

    sanitize-html allowedSchemesAppliedToAttributes = Include URI-bearing attributes such as action, formaction, data, poster, background, ping, xlink:href, dynsrc, lowsrc (instead of only the defaults ['href','src','cite']).
  4. Compensating control

    Use a CSP that mitigates form-based vectors via the CSP form-action directive.

Event History

Jun 12, 2026
CVE Published
via MITRE·08:50 PM
Data Sourced
via MITRE·08:50 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·09:16 PM
DescriptionSeverityWeakness
Jul 31, 2026
Advisory Published
via GitHub·09:43 PM
Data Sourced
via GitHub·09:43 PM
DescriptionSeverityWeaknessAffected Software
Free Weekly Intel

Don't miss critical vulnerabilities

Join thousands of security professionals who receive our weekly digest of trending CVEs, zero-days, and exploited vulnerabilities.

No spam. Unsubscribe anytime.

Frequently Asked Questions

1

What is the severity of CVE-2026-53606?

CVE-2026-53606 has a medium severity rating of 5.4.

2

How do I fix CVE-2026-53606?

To fix CVE-2026-53606, upgrade sanitize-html to version 2.17.5 or later.

3

What software is affected by CVE-2026-53606?

CVE-2026-53606 affects versions of the sanitize-html package prior to 2.17.5.

4

What type of vulnerability is CVE-2026-53606?

CVE-2026-53606 is classified as an XSS vulnerability due to incomplete URI scheme validation.

5

What attributes are vulnerable in CVE-2026-53606?

CVE-2026-53606 allows javascript: URIs through action, formaction, data, poster, and background attributes.

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