Skip to content

Express XSS Sanitizer: allowedTags/allowedAttributes bypass leads to permissive sanitization (XSS risk)

High severity GitHub Reviewed Published Mar 25, 2026 in AhmedAdelFahim/express-xss-sanitizer • Updated Mar 27, 2026

Package

npm express-xss-sanitizer (npm)

Affected versions

< 2.0.2

Patched versions

2.0.2

Description

Description

A vulnerability has been identified in express-xss-sanitizer (<= 2.0.1) where restrictive sanitization configurations are silently ignored.

When a developer explicitly sets:

allowedTags: []
allowedAttributes: {}

the library incorrectly treats these values as "not provided" due to length/emptiness checks, and falls back to sanitize-html's default configuration.

As a result, instead of stripping all HTML tags and attributes, the sanitizer allows a permissive set of tags (e.g., <a>, <p>, <div>, etc.) and attributes (e.g., href on <a>).

This behavior violates the expected API contract and may lead to security issues such as content injection or XSS, depending on how the sanitized output is used.

Impact

Developers intending to fully strip HTML content by providing empty allowedTags or allowedAttributes configurations may unknowingly allow a wide range of HTML elements and attributes.

This can result in:

  • Injection of unintended HTML content (e.g., <div>, <table>, headings)
  • Injection of links via <a href="...">
  • Potential XSS vectors depending on downstream usage

The impact depends on how the sanitized output is rendered or consumed, but the root issue is a mismatch between developer intent and actual behavior.

Proof of Concept

const { sanitize } = require('express-xss-sanitizer');
const sanitizeHtml = require('sanitize-html');

const input = '<a href="http://evil.com">click</a><p>phish</p>';

// Using express-xss-sanitizer (v2.0.1)
sanitize(input, { allowedTags: [], allowedAttributes: {} });
// => '<a href="http://evil.com">click</a><p>phish</p>'

// Expected behavior (sanitize-html directly)
sanitizeHtml(input, { allowedTags: [], allowedAttributes: {} });
// => 'clickphish'

Root Cause

The issue was caused by validation logic that checked for non-empty arrays/objects:

  • allowedTags required length > 0
  • allowedAttributes required Object.keys(...).length > 0

This caused empty configurations ([]) and ({}) to be ignored, resulting in fallback to default permissive settings.

Fix

The validation logic has been updated to respect explicitly provided empty configurations.

Now, if allowedTags or allowedAttributes are provided (even if empty), they are passed directly to sanitize-html without being overridden.

References

Published to the GitHub Advisory Database Mar 27, 2026
Reviewed Mar 27, 2026
Last updated Mar 27, 2026

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
Low
Integrity
High
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N

EPSS score

Weaknesses

Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users. Learn more on MITRE.

Permissive List of Allowed Inputs

The product implements a protection mechanism that relies on a list of inputs (or properties of inputs) that are explicitly allowed by policy because the inputs are assumed to be safe, but the list is too permissive - that is, it allows an input that is unsafe, leading to resultant weaknesses. Learn more on MITRE.

CVE ID

CVE-2026-33979

GHSA ID

GHSA-3843-rr4g-m8jq

Credits

Loading Checking history
See something to contribute? Suggest improvements for this vulnerability.