CVE-2026-49476 Overview
CVE-2026-49476 is a denial of service vulnerability in Soup Sieve, a CSS selector library used with Beautiful Soup 4. The CSS selector parser allocates unbounded memory when compiling large comma-separated selector lists. An attacker who can supply a crafted selector string to soupsieve.compile(), or to Beautiful Soup's .select() or .select_one() methods, can force the process to allocate hundreds of megabytes of heap memory from a relatively small input. The flaw is tracked under [CWE-400] Uncontrolled Resource Consumption and is fixed in version 2.8.4.
Critical Impact
Remote attackers can trigger heap exhaustion and denial of service in any Python service that parses attacker-controlled CSS selectors through Beautiful Soup or Soup Sieve.
Affected Products
- Soup Sieve versions prior to 2.8.4
- Beautiful Soup 4 deployments using vulnerable Soup Sieve versions for .select() and .select_one() calls
- Python applications invoking soupsieve.compile() on untrusted selector input
Discovery Timeline
- 2026-07-14 - CVE-2026-49476 published to NVD
- 2026-07-15 - Last updated in NVD database
Technical Details for CVE-2026-49476
Vulnerability Analysis
Soup Sieve compiles CSS selectors into internal SelectorList objects before matching them against parsed HTML. When a selector contains a large comma-separated list, the parser expands each branch without enforcing an upper bound on the number of compiled selectors. A short input string can therefore produce a SelectorList whose in-memory representation consumes hundreds of megabytes of heap.
The amplification factor between input length and allocated memory makes this vulnerability practical to exploit remotely. Web scrapers, HTML sanitizers, template engines, and any service that accepts user-controlled selectors are exposed. Repeated requests can exhaust available memory and crash the worker process, resulting in denial of service.
Root Cause
The root cause is the absence of a bounds check on the number of selectors produced during compilation of comma-separated selector lists. The SelectorList class in soupsieve/css_types.py did not track or cap the cumulative selector count across nested compilation calls, so recursive expansion of complex selectors grew without limit.
Attack Vector
Exploitation requires only the ability to pass a selector string to a vulnerable API. The attack is network-reachable, requires no privileges, and requires no user interaction. Any HTTP endpoint that forwards user input into soupsieve.compile(), BeautifulSoup.select(), or BeautifulSoup.select_one() can be abused.
# Patch excerpt from soupsieve/css_types.py
class SelectorList(Immutable):
"""Selector list."""
- __slots__ = ("selectors", "is_not", "is_html", "_hash")
+ __slots__ = ("selectors", "is_not", "is_html", "count", "_hash")
selectors: tuple[Selector | SelectorNull, ...]
is_not: bool
is_html: bool
+ count: int
def __init__(
self,
selectors: Iterable[Selector | SelectorNull] | None = None,
is_not: bool = False,
- is_html: bool = False
+ is_html: bool = False,
+ count: int = 0,
) -> None:
"""Initialize."""
super().__init__(
selectors=tuple(selectors) if selectors is not None else (),
is_not=is_not,
- is_html=is_html
+ is_html=is_html,
+ count=count
)
Source: GitHub Commit 28108ab. The patch introduces a count field on SelectorList to track selector cardinality during compilation and enforce an upper bound.
Detection Methods for CVE-2026-49476
Indicators of Compromise
- Sudden spikes in resident set size (RSS) of Python worker processes handling HTML parsing tasks
- MemoryError exceptions or out-of-memory (OOM) kills in application logs referencing soupsieve or bs4.BeautifulSoup.select
- Inbound HTTP requests containing selector parameters with unusually long comma-separated token sequences
- Sustained high CPU usage during selector compilation followed by process termination
Detection Strategies
- Inventory Python dependencies with pip list | grep soupsieve and flag any version below 2.8.4
- Instrument application code paths that call soupsieve.compile(), .select(), or .select_one() to log selector length and comma counts
- Add web application firewall (WAF) rules that reject selector-shaped parameters exceeding a defined length threshold
Monitoring Recommendations
- Track heap allocation and OOM events on hosts running Beautiful Soup workloads using process-level telemetry
- Alert on repeated 5xx responses correlated with worker restarts from the same source IP
- Monitor container memory limits and restart counts for services that expose HTML or selector processing endpoints
How to Mitigate CVE-2026-49476
Immediate Actions Required
- Upgrade Soup Sieve to version 2.8.4 or later across all Python environments
- Audit all application code that accepts selector strings from external sources and validate input length before compilation
- Apply request size and rate limits at the reverse proxy or WAF for endpoints that consume selectors
- Restart long-running Python processes after upgrading to ensure the patched library is loaded
Patch Information
The fix is available in Soup Sieve 2.8.4, released via GitHub Release 2.8.4. Technical details are documented in GitHub Security Advisory GHSA-2wc2-fm75-p42x. Upgrade with pip install --upgrade soupsieve>=2.8.4 and rebuild any container images that pin an older version.
Workarounds
- Reject selector strings longer than a conservative byte limit (for example, 1024 bytes) before passing them to Soup Sieve
- Cap the number of commas allowed in a selector expression to bound the compiled selector count
- Isolate HTML parsing workloads in memory-limited containers or subprocesses so an OOM does not affect the parent service
# Upgrade Soup Sieve to the patched version
pip install --upgrade 'soupsieve>=2.8.4'
# Verify the installed version
python -c "import soupsieve; print(soupsieve.__version__)"
# Freeze the pinned version in requirements.txt
echo 'soupsieve>=2.8.4' >> requirements.txt
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

