high · 7.5CVE-2026-49477Jul 9, 2026

CVE-2026-49477: soupsieve Regular Expression Denial of Service (ReDoS)

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

A broken regex in soupsieve's CSS selector parser hangs indefinitely on a 300-byte malformed attribute selector, letting any attacker block a server thread with a single HTTP request.

Packagesoupsieve
Ecosystempip
Affected<= 2.8.3
Fixed in2.8.4

The problem

The `VALUE` regex in `soupsieve/css_parser.py` tokenises attribute selector values. It has separate alternation branches for double-quoted strings, single-quoted strings, and unquoted identifiers.

When the input contains an opening double-quote but no closing quote, the regex engine fails the quoted-string branch and backtracks into the remaining branches. The overlapping character classes cause exponential backtracking. A 300-byte payload produces more than 3 seconds of CPU spin, and each additional byte roughly doubles the cost.

Any application that passes user-supplied CSS selectors to `soupsieve.compile()`, `BeautifulSoup.select()`, or `select_one()` is exposed.

Proof of concept

A working proof-of-concept for CVE-2026-49477 in soupsieve, with the exact payload below.

python
import soupsieve as sv

# Unterminated double-quoted attribute value: no closing "
malformed = '[a="' + ('x' * 300)

# WARNING: on vulnerable versions (<=2.8.3) this call will hang for >3 seconds
# and may need to be killed. Use signal.alarm() or threading.Timer for safe testing.
sv.compile(malformed)

The vulnerable `RE_VALUES` / `VALUE` pattern in `css_parser.py` (around line 121) matches quoted strings with the classic Friedl "string with escapes" pattern: `"[^"\\]*(?:\\.[^"\\]*)*"`. When the closing `"` is absent the engine exhausts every combination of the inner `(?:\\.[^"\\]*)` repetition against the content, producing O(2^n) backtracking steps.

The 2.8.4 patch rewrites the attribute-value regex to eliminate the ambiguous alternation, either by anchoring the quoted-string branch so it fails fast on unterminated input (possessive quantifiers or atomic groups) or by pre-validating that a matching closing quote is present before running the main pattern.

CWE-1333 (Inefficient Regular Expression Complexity) is the root cause; no other code paths are affected.

The fix

Upgrade to soupsieve 2.8.4 (`pip install --upgrade soupsieve`). If you cannot upgrade immediately, reject any selector input that contains an odd number of unescaped quote characters before passing it to soupsieve. Also audit any Beautiful Soup dependency that pulls soupsieve transitively.

Reported by Liyi Zhou, Ziyue Wang, Strick, Maurice, Chenchen Yu (University of Sydney Security Research Team).

References: [1][2]

Related research