⬆️ Updates soupsieve to v2.9 [SECURITY] - #3566
renovate[bot] wants to merge 1 commit into
Conversation
Branch automerge failureThis PR was configured for branch automerge. However, this is not possible, so it has been raised as a PR instead.
|
|
Dependency limit exceeded — report not shown. This pull request scan exceeded the 10,000-dependency limit applied to this scan, so the results are incomplete and may be inaccurate. To avoid reporting false positives, Socket has not posted a report. Upgrade your plan to raise the dependency limit and get complete reports, or view the partial scan in the dashboard. Socket is always free for open source. If this is a non-commercial open source project, contact us to request a free Team account. |
e944e2d to
5a37a50
Compare
There was a problem hiding this comment.
Scan Summary
| Tool | Critical | High | Medium | Low | Status |
|---|---|---|---|---|---|
| Dependency Scan (universal) | 2 | 9 | 5 | 0 | ❌ |
| Secrets Audit | 0 | 5 | 0 | 0 | ❌ |
| Kotlin Security Audit | 0 | 0 | 0 | 0 | ✅ |
| Security Audit for Infrastructure | 14 | 92 | 8 | 32 | ❌ |
| Shell Script Analysis | 0 | 0 | 0 | 195 | ✅ |
| Kotlin Static Analysis | 0 | 0 | 0 | 0 | ✅ |
| Python Source Analyzer | 0 | 0 | 0 | 0 | ✅ |
Recommendation
Please review the findings from Code scanning alerts before approving this pull request. You can also configure the build rules or add suppressions to customize this bot 👍
5a37a50 to
c949498
Compare
There was a problem hiding this comment.
Scan Summary
| Tool | Critical | High | Medium | Low | Status |
|---|---|---|---|---|---|
| Dependency Scan (universal) | 2 | 12 | 6 | 0 | ❌ |
| Kotlin Security Audit | 0 | 0 | 0 | 0 | ✅ |
| Security Audit for Infrastructure | 14 | 92 | 8 | 32 | ❌ |
| Python Source Analyzer | 0 | 0 | 0 | 0 | ✅ |
| Secrets Audit | 0 | 5 | 0 | 0 | ❌ |
| Shell Script Analysis | 0 | 0 | 0 | 195 | ✅ |
| Kotlin Static Analysis | 0 | 0 | 0 | 0 | ✅ |
Recommendation
Please review the findings from Code scanning alerts before approving this pull request. You can also configure the build rules or add suppressions to customize this bot 👍
c949498 to
bd337be
Compare
There was a problem hiding this comment.
Scan Summary
| Tool | Critical | High | Medium | Low | Status |
|---|---|---|---|---|---|
| Dependency Scan (universal) | 2 | 7 | 6 | 1 | ❌ |
| Kotlin Security Audit | 0 | 0 | 0 | 0 | ✅ |
| Security Audit for Infrastructure | 14 | 92 | 8 | 32 | ❌ |
| Python Source Analyzer | 0 | 0 | 0 | 0 | ✅ |
| Secrets Audit | 0 | 5 | 0 | 0 | ❌ |
| Shell Script Analysis | 0 | 0 | 0 | 195 | ✅ |
| Kotlin Static Analysis | 0 | 0 | 0 | 0 | ✅ |
Recommendation
Please review the findings from Code scanning alerts before approving this pull request. You can also configure the build rules or add suppressions to customize this bot 👍
bd337be to
dc834c2
Compare
There was a problem hiding this comment.
Scan Summary
| Tool | Critical | High | Medium | Low | Status |
|---|---|---|---|---|---|
| Dependency Scan (universal) | 2 | 13 | 7 | 0 | ❌ |
| Kotlin Security Audit | 0 | 0 | 0 | 0 | ✅ |
| Security Audit for Infrastructure | 14 | 92 | 8 | 32 | ❌ |
| Python Source Analyzer | 0 | 0 | 0 | 0 | ✅ |
| Secrets Audit | 0 | 5 | 0 | 0 | ❌ |
| Shell Script Analysis | 0 | 0 | 0 | 195 | ✅ |
| Kotlin Static Analysis | 0 | 0 | 0 | 0 | ✅ |
Recommendation
Please review the findings from Code scanning alerts before approving this pull request. You can also configure the build rules or add suppressions to customize this bot 👍
3a4fb3b to
6e65c08
Compare
There was a problem hiding this comment.
Scan Summary
| Tool | Critical | High | Medium | Low | Status |
|---|---|---|---|---|---|
| Dependency Scan (universal) | 2 | 7 | 6 | 0 | ❌ |
| Python Source Analyzer | 0 | 0 | 0 | 0 | ✅ |
| Kotlin Static Analysis | 0 | 0 | 0 | 0 | ✅ |
| Security Audit for Infrastructure | 14 | 92 | 8 | 32 | ❌ |
| Shell Script Analysis | 0 | 0 | 0 | 195 | ✅ |
| Kotlin Security Audit | 0 | 0 | 0 | 0 | ✅ |
| Secrets Audit | 0 | 5 | 0 | 0 | ❌ |
Recommendation
Please review the findings from Code scanning alerts before approving this pull request. You can also configure the build rules or add suppressions to customize this bot 👍
6e65c08 to
b567edf
Compare
There was a problem hiding this comment.
Scan Summary
| Tool | Critical | High | Medium | Low | Status |
|---|---|---|---|---|---|
| Dependency Scan (universal) | 2 | 6 | 6 | 0 | ❌ |
| Python Source Analyzer | 0 | 0 | 0 | 0 | ✅ |
| Kotlin Static Analysis | 0 | 0 | 0 | 0 | ✅ |
| Security Audit for Infrastructure | 14 | 92 | 8 | 32 | ❌ |
| Shell Script Analysis | 0 | 0 | 0 | 195 | ✅ |
| Kotlin Security Audit | 0 | 0 | 0 | 0 | ✅ |
| Secrets Audit | 0 | 5 | 0 | 0 | ❌ |
Recommendation
Please review the findings from Code scanning alerts before approving this pull request. You can also configure the build rules or add suppressions to customize this bot 👍
bda35c0 to
ebacf39
Compare
ebacf39 to
d2966aa
Compare
e3ff031 to
36e19c9
Compare
There was a problem hiding this comment.
Scan Summary
| Tool | Critical | High | Medium | Low | Status |
|---|---|---|---|---|---|
| Dependency Scan (universal) | 3 | 13 | 7 | 1 | ❌ |
| Python Source Analyzer | 0 | 0 | 0 | 0 | ✅ |
| Kotlin Static Analysis | 0 | 0 | 0 | 0 | ✅ |
| Security Audit for Infrastructure | 14 | 92 | 8 | 32 | ❌ |
| Shell Script Analysis | 0 | 0 | 0 | 195 | ✅ |
| Kotlin Security Audit | 0 | 0 | 0 | 0 | ✅ |
| Secrets Audit | 0 | 4 | 0 | 0 | ❌ |
Recommendation
Please review the findings from Code scanning alerts before approving this pull request. You can also configure the build rules or add suppressions to customize this bot 👍
36e19c9 to
ab14ba2
Compare
There was a problem hiding this comment.
Scan Summary
| Tool | Critical | High | Medium | Low | Status |
|---|---|---|---|---|---|
| Dependency Scan (universal) | 4 | 13 | 8 | 0 | ❌ |
| Python Source Analyzer | 0 | 0 | 0 | 0 | ✅ |
| Kotlin Static Analysis | 0 | 0 | 0 | 0 | ✅ |
| Security Audit for Infrastructure | 14 | 92 | 8 | 32 | ❌ |
| Shell Script Analysis | 0 | 0 | 0 | 195 | ✅ |
| Kotlin Security Audit | 0 | 0 | 0 | 0 | ✅ |
| Secrets Audit | 0 | 4 | 0 | 0 | ❌ |
Recommendation
Please review the findings from Code scanning alerts before approving this pull request. You can also configure the build rules or add suppressions to customize this bot 👍
ab14ba2 to
026880d
Compare
There was a problem hiding this comment.
Scan Summary
| Tool | Critical | High | Medium | Low | Status |
|---|---|---|---|---|---|
| Dependency Scan (universal) | 2 | 6 | 8 | 0 | ❌ |
| Shell Script Analysis | 0 | 0 | 0 | 195 | ✅ |
| Python Source Analyzer | 0 | 0 | 0 | 0 | ✅ |
| Security Audit for Infrastructure | 14 | 92 | 8 | 32 | ❌ |
| Secrets Audit | 0 | 4 | 0 | 0 | ❌ |
| Kotlin Security Audit | 0 | 0 | 0 | 0 | ✅ |
| Kotlin Static Analysis | 0 | 0 | 0 | 0 | ✅ |
Recommendation
Please review the findings from Code scanning alerts before approving this pull request. You can also configure the build rules or add suppressions to customize this bot 👍
7a65f8f to
c5deff7
Compare
There was a problem hiding this comment.
Scan Summary
| Tool | Critical | High | Medium | Low | Status |
|---|---|---|---|---|---|
| Dependency Scan (universal) | 3 | 16 | 10 | 0 | ❌ |
| Shell Script Analysis | 0 | 0 | 0 | 195 | ✅ |
| Python Source Analyzer | 0 | 0 | 0 | 0 | ✅ |
| Security Audit for Infrastructure | 14 | 92 | 8 | 32 | ❌ |
| Secrets Audit | 0 | 4 | 0 | 0 | ❌ |
| Kotlin Security Audit | 0 | 0 | 0 | 0 | ✅ |
| Kotlin Static Analysis | 0 | 0 | 0 | 0 | ✅ |
Recommendation
Please review the findings from Code scanning alerts before approving this pull request. You can also configure the build rules or add suppressions to customize this bot 👍
c5deff7 to
9a39834
Compare
There was a problem hiding this comment.
Scan Summary
| Tool | Critical | High | Medium | Low | Status |
|---|---|---|---|---|---|
| Dependency Scan (universal) | 2 | 11 | 9 | 0 | ❌ |
| Shell Script Analysis | 0 | 0 | 0 | 195 | ✅ |
| Python Source Analyzer | 0 | 0 | 0 | 0 | ✅ |
| Security Audit for Infrastructure | 14 | 92 | 8 | 32 | ❌ |
| Secrets Audit | 0 | 4 | 0 | 0 | ❌ |
| Kotlin Security Audit | 0 | 0 | 0 | 0 | ✅ |
| Kotlin Static Analysis | 0 | 0 | 0 | 0 | ✅ |
Recommendation
Please review the findings from Code scanning alerts before approving this pull request. You can also configure the build rules or add suppressions to customize this bot 👍
9a39834 to
ba4ca30
Compare
There was a problem hiding this comment.
Scan Summary
| Tool | Critical | High | Medium | Low | Status |
|---|---|---|---|---|---|
| Dependency Scan (universal) | 2 | 8 | 7 | 0 | ❌ |
| Shell Script Analysis | 0 | 0 | 0 | 195 | ✅ |
| Python Source Analyzer | 0 | 0 | 0 | 0 | ✅ |
| Security Audit for Infrastructure | 14 | 92 | 8 | 32 | ❌ |
| Secrets Audit | 0 | 4 | 0 | 0 | ❌ |
| Kotlin Security Audit | 0 | 0 | 0 | 0 | ✅ |
| Kotlin Static Analysis | 0 | 0 | 0 | 0 | ✅ |
Recommendation
Please review the findings from Code scanning alerts before approving this pull request. You can also configure the build rules or add suppressions to customize this bot 👍
ebe4585 to
0bd8647
Compare
There was a problem hiding this comment.
Scan Summary
| Tool | Critical | High | Medium | Low | Status |
|---|---|---|---|---|---|
| Dependency Scan (universal) | 2 | 7 | 6 | 0 | ❌ |
| Shell Script Analysis | 0 | 0 | 0 | 195 | ✅ |
| Python Source Analyzer | 0 | 0 | 0 | 0 | ✅ |
| Security Audit for Infrastructure | 14 | 92 | 8 | 32 | ❌ |
| Secrets Audit | 0 | 4 | 0 | 0 | ❌ |
| Kotlin Security Audit | 0 | 0 | 0 | 0 | ✅ |
| Kotlin Static Analysis | 0 | 0 | 0 | 0 | ✅ |
Recommendation
Please review the findings from Code scanning alerts before approving this pull request. You can also configure the build rules or add suppressions to customize this bot 👍
3b22b02 to
4566c32
Compare
There was a problem hiding this comment.
Scan Summary
| Tool | Critical | High | Medium | Low | Status |
|---|---|---|---|---|---|
| Dependency Scan (universal) | 2 | 6 | 7 | 0 | ❌ |
| Shell Script Analysis | 0 | 0 | 0 | 195 | ✅ |
| Python Source Analyzer | 0 | 0 | 0 | 0 | ✅ |
| Security Audit for Infrastructure | 14 | 92 | 8 | 32 | ❌ |
| Secrets Audit | 0 | 4 | 0 | 0 | ❌ |
| Kotlin Security Audit | 0 | 0 | 0 | 0 | ✅ |
| Kotlin Static Analysis | 0 | 0 | 0 | 0 | ✅ |
Recommendation
Please review the findings from Code scanning alerts before approving this pull request. You can also configure the build rules or add suppressions to customize this bot 👍
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
4566c32 to
f76160c
Compare
There was a problem hiding this comment.
Scan Summary
| Tool | Critical | High | Medium | Low | Status |
|---|---|---|---|---|---|
| Dependency Scan (universal) | 2 | 7 | 9 | 0 | ❌ |
| Shell Script Analysis | 0 | 0 | 0 | 195 | ✅ |
| Python Source Analyzer | 0 | 0 | 0 | 0 | ✅ |
| Security Audit for Infrastructure | 14 | 92 | 8 | 32 | ❌ |
| Secrets Audit | 0 | 4 | 0 | 0 | ❌ |
| Kotlin Security Audit | 0 | 0 | 0 | 0 | ✅ |
| Kotlin Static Analysis | 0 | 0 | 0 | 0 | ✅ |
Recommendation
Please review the findings from Code scanning alerts before approving this pull request. You can also configure the build rules or add suppressions to customize this bot 👍
This PR contains the following updates:
==2.3.2.post1→==2.9Warning
Some dependencies could not be looked up. Check the Dependency Dashboard for more information.
Soup Sieve: Regular Expression Denial of Service (ReDoS) via Selector Parser
CVE-2026-49477 / GHSA-836r-79rf-4m37
More information
Details
Summary
The CSS selector parser in soupsieve (the CSS selector engine for Beautiful Soup 4) contains a regular expression vulnerable to catastrophic backtracking. When processing an attribute selector with an unterminated quoted value, the
VALUEregex pattern incss_parser.pyenters exponential backtracking. A payload of only 300 bytes causes the regex engine to hang for over 3 seconds, enabling a trivial Regular Expression Denial of Service (ReDoS) attack.To be completely transparent, AI tools helped surface this issue. However, this was independently reproduced and carefully validated.
Any application that passes untrusted CSS selector strings to
soupsieve.compile()or Beautiful Soup's.select()/.select_one()is affected.Details
Affected code:
soupsieve/css_parser.py, line ~121 -RE_VALUES/VALUEregex patternThe soupsieve CSS parser uses a compiled regular expression to tokenise attribute selector values. This pattern matches both quoted strings (
"value"or'value') and unquoted identifiers. The regex contains alternation branches for:"[^"\\]*(?:\\.[^"\\]*)*"'[^'\\]*(?:\\.[^'\\]*)*'When an attribute selector contains an unterminated quoted value - e.g.,
[a="xxxx...(opening"but no closing") -” the regex engine attempts to match the quoted-string branch. After that branch fails (no closing quote), the engine backtracks and attempts to match the remaining input against subsequent alternation branches and parent patterns. The structure of the pattern causes catastrophic backtracking where the number of backtracking steps grows exponentially with the length of the content between the opening quote and the end of the string.Root cause: The regex pattern does not anchor or guard against the case where a quoted string is never terminated. The overlapping character classes across alternation branches create exponential backtracking when the quoted-string branch fails on long input.
Key characteristics:
Proof of Concept
Safe testing variant with timeout:
Impact
Severity: High
An attacker can cause CPU exhaustion on any server-side Python application that compiles user-supplied CSS selectors via soupsieve. The attack is particularly dangerous because:
[a="xxx...)Deployment impact: In threaded or async web applications, a single malicious request blocks a worker thread for the duration of the backtracking. An attacker can submit multiple concurrent requests to exhaust all available workers, causing complete service denial. The small payload size makes the attack easy to deliver and difficult to detect via request size limits.
Downstream exposure: soupsieve is an automatic dependency of
beautifulsoup4, one of the most widely installed Python packages. Any web application, API, or service that accepts CSS selectors from users is potentially affected.Credit
The vulnerability was discovered by a security research team from the University of Sydney, whose focus is detecting open source software vulnerabilities.
Liyi Zhou: https://lzhou1110.github.io/
Ziyue Wang: https://zyy0530.github.io/
Strick: https://str1ckl4nd.github.io/
Maurice: https://maurice.busystar.org/
Chenchen Yu: https://7thparkk.github.io/
Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:HReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Soup Sieve has Memory Exhaustion via Large Comma-Separated Selector Lists
CVE-2026-49476 / GHSA-2wc2-fm75-p42x
More information
Details
Summary
The CSS selector parser in soupsieve (the CSS selector engine for Beautiful Soup 4) allocates unbounded memory when compiling large comma-separated selector lists. An attacker who can supply a crafted CSS selector string to
soupsieve.compile()or Beautiful Soup's.select()/.select_one()can cause the application to allocate hundreds of megabytes of heap memory from a relatively small input, leading to memory exhaustion and denial of service.To be completely transparent, AI tools helped surface this issue. However, it was independently reproduced and carefully validated. Researchers follow responsible disclosure practices and originally shared this report privately.
A 500 KB selector string triggers allocation of approximately 244 MB of heap memory - a 488x— amplification ratio**.
Details
Affected code:
soupsieve/css_parser.py, lines ~204, 925, 1106The soupsieve CSS parser splits comma-separated selector lists and creates one
CSSSelectorobject per list item. EachCSSSelectorobject contains parsed selector data structures includingSelectorList,Selector, and associated tag/attribute/pseudo-class metadata.When a selector string such as
a,a,a,...(with 250,000 comma-separated items) is passed tosv.compile(), the parser:Selectorobject with all associated metadata (line ~925)SelectorList(line ~204)Root cause: No limit is enforced on the number of selectors in a comma-separated list. The parser will attempt to parse and store an arbitrary number of selectors, with each selector object consuming approximately 976 bytes of heap memory. The total allocation scales linearly with the number of list items, but the amplification ratio (output memory / input bytes) is extremely high because each single-character selector like
aexpands into a complex object graph.Attack surface: Any application that passes user-supplied CSS selectors to
soupsieve.compile()or Beautiful Soup's.select()/.select_one().Proof of Concept
Impact
Severity: High
An attacker can exhaust available memory on any server-side Python application that compiles user-supplied CSS selectors via soupsieve. This can cause:
MemoryErrorexception if the system runs out of addressable memoryScalability of attack: The memory allocation scales linearly - doubling the selector count doubles memory usage. An attacker can tune the payload to exactly exhaust a target's memory limits. Multiple concurrent requests multiply the effect.
Downstream exposure: soupsieve is an automatic dependency of
beautifulsoup4, one of the most widely installed Python packages. Any web application accepting CSS selectors from users (e.g., web scraping APIs, content filtering tools, CMS preview features) is potentially affected.Credit
Discovered by a security research team from the University of Sydney, focused on detecting open source software vulnerabilities.
Liyi Zhou: https://lzhou1110.github.io/
Ziyue Wang: https://zyy0530.github.io/
Strick: https://str1ckl4nd.github.io/
Maurice: https://maurice.busystar.org/
Chenchen Yu: https://7thparkk.github.io/
Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:HReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Soup Sieve: Polynomial-time ReDoS (O(n²)) in the
IDENTIFIER/VALUEselector sub-patternsCVE-2026-86000 / GHSA-gjv8-xp57-g29c
More information
Details
Summary
soupsieve compiles CSS selector strings with a set of hand-written regular expressions. The shared
IDENTIFIERsub-pattern (also embedded inVALUE, and therefore in attribute selectors) places two adjacent quantified groups over overlapping character classes:(?:[classA]|ESC)+(?:[classB]|ESC)*, where both classes match ordinary identifier characters such asa. When a selector contains a long identifier/value run that must ultimately fail to match (e.g. an attribute value with no closing], or an identifier followed by an invalid character), the regex engine backtracks across all O(n) ways to split the run between the+group and the*group, giving O(n²) parse time. A single attacker-controlled selector of a few kilobytes stalls the interpreter for many seconds of CPU; tens of kilobytes reach minutes.Trust model (Q0)
The selector string is the input. It reaches this code via
soupsieve.compile(),soupsieve.select/iselect/match/filter, and — most commonly — BeautifulSoup'ssoup.select(selector)/soup.select_one(selector), which delegate to soupsieve. This is exploitable in any application that passes a user-controlled CSS selector to BeautifulSoup/soupsieve (scrapers that accept selectors, no-code extraction tools, admin/query UIs). Applications that only use hard-coded selectors are not affected.Root cause (exact anchors) —
src/soupsieve/css_parser.py[^\x00-\x2f\x30-\x40\x5B-\x5E\x60\x7B-\x9f]excludes digits (0x30-0x39); classB[^\x00-\x2c\x2e\x2f\x3A-\x40\x5B-\x5E\x60\x7B-\x9f]allows digits. The intent is "first char not a digit, remaining chars may be digits."a= 0x61). The construct is therefore effectively(?:C)+(?:C)*over an overlapping class C — the canonical adjacent-quantifier shape that backtracks quadratically on a failing match.The quadratic only manifests when the overall match must fail.
IDENTIFIERmatched greedily on"a"*nsucceeds in linear time (~1 ms at n=32000). Anchoring it so a following element is mandatory and fails (IDENTIFIER + "$"against"a"*n + "!") reproduces the O(n²) directly: n=2000 → 44 ms, 4000 → 257 ms, 8000 → 743 ms, 16000 → 2944 ms (~×4 per ×2). Profilingcompile("[a=" + "a"*4000)shows only 12re.matchcalls consuming 2.685 s — i.e. the cost is inside a single regex match, confirming regex backtracking (not loop overhead).Reproduction environment (discipline #12 — published artifact)
751c57b(2.9,PYTHONPATH=src):cd src && python3 ../poc/poc_redos_compile.py.soupsieve 2.8.4(freshuv pip install soupsieve beautifulsoup4):cd poc && ../.venv-published/bin/python poc_redos_compile.py→ same O(n²) (evidence:poc/evidence_redos_compile_PUBLISHED_2.8.4.log).PoC (
poc/poc_redos_compile.py)End-to-end note:
bs4.BeautifulSoup(html).select(payload)reaches the samecompile()path, so the stall is triggerable directly through BeautifulSoup with a user-supplied selector. Verified on bs4 4.15.0 + soupsieve 2.8.4:soup.select("[a=" + "a"*6000)took ~5.0 s for one call (evidence:poc/evidence_bs4_select_PUBLISHED_2.8.4.log).Evidence — HEAD 2.9 (verbatim
poc/evidence_redos_compile.log)Evidence — published 2.8.4 (verbatim
poc/evidence_redos_compile_PUBLISHED_2.8.4.log)Impact — calibrated
compile()/select()call on an attacker-controlled selector. ~8 KB → ~8 s; ~12 KB → ~17 s; scaling ~×4 per input doubling. A handful of such requests exhausts a worker/thread and degrades or stalls the service (single-threaded regex holds the GIL).Remediation
IDENTIFIER: match a single leading non-digit character then the remaining class once, e.g.(?:-?(?:[classA]|ESC)(?:[classB]|ESC)*|--(?:[classB]|ESC)*), so no+/*pair spans the same characters.(?>...),*+) to forbid backtracking into the identifier run.Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:LReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Soup Sieve: Polynomial-time ReDoS (O(n²)) in the whitespace/comment trimming regex
RE_WS_END(triggers on VALID selectors)CVE-2026-85999 / GHSA-j934-xhv5-fg8f
More information
Details
Summary
Before tokenizing,
selector_itertrims leading/trailing whitespace and comments by running two regexes over the whole raw selector with.search(). The trailing one,RE_WS_END = re.compile(r'{WSC}*$'), is anchored only at the end ($), not the start. Because.search()retries the pattern at every offset, a long run of whitespace or CSS comments that is not sitting exactly at the end of the string makes each retry greedily consume the run and then fail$, producing O(n²) time. This triggers on perfectly valid selectors — e.g. a descendant combinator with a long whitespace gap,a+" "*n+b— so no malformed input is required. A single valid ~20 KB selector stalls the interpreter for ~10 s of CPU.Trust model (Q0)
The selector string is the input, reaching this code via
soupsieve.compile(), thesoupsieve.select/iselect/match/filterhelpers, and BeautifulSoup'ssoup.select(selector)/soup.select_one(selector). Exploitable wherever an application passes a user-controlled CSS selector to BeautifulSoup/soupsieve. Applications using only hard-coded selectors are unaffected.Root cause (exact anchors) —
src/soupsieve/css_parser.pyWSC = (?:{WS}|{COMMENTS}). ForRE_WS_END = (?:WS|COMMENTS)*$,.search()walks start offsets 0..n. Whenever the offset lands inside a long whitespace/comment run,(?:WS|COMMENTS)*greedily consumes to the run's end, then$fails (a non-whitespace char follows), the engine backtracks the whole run, the offset advances by one, and the work repeats — O(n) offsets × O(n) per attempt = O(n²).RE_WS_BEGINavoids this because^pins it to a single start offset.The intent (trim trailing whitespace/comments) can be met with an anchored/loopless approach; the current unanchored
.search()of a*$pattern is the defect.Reproduction environment (discipline #12 — published artifact)
751c57b(2.9,PYTHONPATH=src):cd src && python3 ../poc/poc_redos_ws_trim.py.soupsieve 2.8.4(freshuv pip install soupsieve beautifulsoup4):cd poc && ../.venv-published/bin/python poc_redos_ws_trim.py→ same O(n²) (evidence:poc/evidence_redos_ws_trim_PUBLISHED_2.8.4.log).PoC (
poc/poc_redos_ws_trim.py)Isolated confirmation that the cost is in
RE_WS_END.searchspecifically (poc/isolate_ws_trim.py):RE_WS_ENDon"div"+" "*n+">"is O(n²) (2000→100 ms, 4000→448 ms, 8000→1622 ms, 16000→6719 ms), while the start-anchoredRE_WS_BEGINon" "*n+"x"stays linear (32000→1.5 ms). Profilingcompileshows the entire wall time in 2re.Pattern.searchcalls, not.match.Evidence — HEAD 2.9 (verbatim
poc/evidence_redos_ws_trim.log)Evidence — published 2.8.4 (verbatim
poc/evidence_redos_ws_trim_PUBLISHED_2.8.4.log)Impact — calibrated
compile()/select()call on an attacker-controlled selector, triggered by a long internal whitespace or CSS-comment run. ~8 KB → ~1.6 s; ~20 KB → ~10 s; scaling ~×4 per input doubling. Notably fires on WELL-FORMED selectors, so it does not depend on a parser error path.Distinction from the IDENTIFIER/VALUE ReDoS
This is a separate root cause and a separate fix: the cost here is entirely in the
RE_WS_END = {WSC}*$trim step run with.search()before tokenizing (measured inre.Pattern.search), whereas the IDENTIFIER/VALUE issue is adjacent-quantifier backtracking during token.match(). They can be fixed independently.Remediation
.search()of{WSC}*$, scan trailing whitespace/comments from the end directly (e.g. reverse scan, orre.compile(r'^{WSC}*').matchon a reversed-equivalent), so no per-offset retry occurs.*$search.Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:LReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Release Notes
facelessuser/soupsieve (soupsieve)
v2.9Compare Source
2.9
:nth-child/:nth-of-type(and-last-variants) forAn+Bvalues whose sequence steps ontoindex 0 or onto the last child (e.g.
:nth-child(2n-2),:nth-child(n-1),:nth-child(n+5)), which previouslymatched the wrong elements or nothing at all (@gaoflow).
v2.8.4Compare Source
2.8.4
v2.8.3Compare Source
2.8.3
v2.8.2Compare Source
2.8.2
:in-rangeand:out-of-rangewith end of year weeks (@mundanevision20).v2.8.1Compare Source
2.8.1
v2.8Compare Source
2.8
v2.7Compare Source
2.7
:openpseudo selector.:mutedpseudo selector.:autofill,:buffering,:fullscreen,:picture-in-picture,:popover-open,:seeking,:stalled, and:volume-locked. These selectors, while recognized, will not match anyelement as they require a live environment to check element states and browser states. This just prevents Soup Sieve
from failing when any of these selectors are specified.
v2.6Compare Source
2.6
&as scoping root per the CSS Nesting Module, Level 1. When&is used outside thecontext of nesting, it is treated as the scoping root (equivalent to
:scope).v2.5Compare Source
2.5
v2.4.1Compare Source
2.4.1
v2.4Compare Source
2.4
:lang()in the official CSS spec.:lang("")should match unspecifiedlanguages, e.g.
lang="", but notlang=und.:is()and:where()should allow forgiving selector lists according to latest CSS (as far as SoupSieve supports "forgiving" which is limited to empty selectors).
Configuration
📅 Schedule: (in timezone Europe/Moscow)
🚦 Automerge: Enabled.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.