Skip to content

⬆️ Updates soupsieve to v4 [SECURITY] - #3566

Open
renovate[bot] wants to merge 1 commit into
masterfrom
renovate/pypi-soupsieve-vulnerability
Open

renovate[bot] wants to merge 1 commit into
masterfrom
renovate/pypi-soupsieve-vulnerability

Conversation

@renovate

@renovate renovate Bot commented Jul 11, 2026 •

Copy link
Copy Markdown
Contributor

Note

This PR body was truncated due to platform limits.

This PR contains the following updates:

Package Change Age Confidence
soupsieve ==2.3.2.post1 → ==4.0 age confidence

Warning

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 VALUE regex pattern in css_parser.py enters 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 / VALUE regex pattern

The 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:

  1. Double-quoted strings: "[^"\\]*(?:\\.[^"\\]*)*"
  2. Single-quoted strings: '[^'\\]*(?:\\.[^'\\]*)*'
  3. Unquoted identifiers

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:

  • Input size: Only 300 bytes are needed to trigger a >3 second hang
  • Amplification: Each additional character approximately doubles the backtracking time
  • No memory impact: The attack consumes CPU only (regex backtracking is compute-bound)
Proof of Concept
import time
import soupsieve as sv

PAYLOAD_LEN = 300

##### Control: well-formed selector with terminated quote (completes instantly)
well_formed = '[a="' + ('x' * PAYLOAD_LEN) + '"]'
start = time.perf_counter()
try:
    sv.compile(well_formed)
except Exception:
    pass
control_time = time.perf_counter() - start
print(f"Well-formed selector ({len(well_formed)} bytes): {control_time:.4f}s")

##### Exploit: unterminated quote triggers catastrophic regex backtracking
malformed = '[a="' + ('x' * PAYLOAD_LEN)
start = time.perf_counter()
try:
    sv.compile(malformed)  # WARNING: This will hang for >3 seconds
except Exception:
    pass
exploit_time = time.perf_counter() - start
print(f"Malformed selector ({len(malformed)} bytes): {exploit_time:.4f}s")

slowdown = exploit_time / max(control_time, 1e-9)
print(f"Slowdown: {slowdown:.0f}x")

##### Expected output:

##### Well-formed selector (306 bytes): ~0.001s
##### Malformed selector (304 bytes): >3.0s (may need to be killed)

##### Slowdown: >3000x
#

##### NOTE: On some systems the malformed selector may hang indefinitely.
##### Use a timeout mechanism (signal.alarm, threading.Timer) when testing.

Safe testing variant with timeout:

import signal
import soupsieve as sv

def timeout_handler(signum, frame):
    raise TimeoutError("ReDoS confirmed: regex backtracking exceeded timeout")

PAYLOAD_LEN = 300
malformed = '[a="' + ('x' * PAYLOAD_LEN)

signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(3)  # 3-second timeout

try:
    sv.compile(malformed)
    print("Selector compiled (not vulnerable)")
except TimeoutError as e:
    print(f"VULNERABLE: {e}")
except Exception as e:
    print(f"Other error: {e}")
finally:
    signal.alarm(0)  # Cancel the alarm
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:

  1. Tiny payload: Only 300 bytes are needed - well within typical URL parameter, form field, or API request limits
  2. No special characters: The payload consists entirely of printable ASCII characters ([a="xxx...)
  3. Exponential scaling: Each additional byte approximately doubles the backtracking time, making the attack easily tuneable
  4. Thread blocking: The regex engine blocks the calling thread with no opportunity for interruption (except via OS signals)
Parameter Value
Input size 300 bytes
CPU time consumed >3 seconds (exponential with payload length)
Memory consumed Negligible (CPU-only attack)
Authentication required None
User interaction required None

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 Score: 7.5 / 10 (High)
  • Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

References

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, 1106

The soupsieve CSS parser splits comma-separated selector lists and creates one CSSSelector object per list item. Each CSSSelector object contains parsed selector data structures including SelectorList, 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 to sv.compile(), the parser:

  1. Tokenises the entire string and identifies each comma-delimited segment (line ~1106)
  2. Parses each segment into a full Selector object with all associated metadata (line ~925)
  3. Stores all parsed selectors in a 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 a expands 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
import tracemalloc
import soupsieve as sv

tracemalloc.start()

##### Build a 500 KB selector string: "a,a,a,...,a" (250,000 items)
count = 250_000
selector = ",".join("a" for _ in range(count))
print(f"Selector string size: {len(selector):,} bytes ({len(selector) / 1024:.0f} KB)")

##### Compile the selector — this allocates ~244 MB
compiled = sv.compile(selector)

current, peak = tracemalloc.get_traced_memory()
tracemalloc.stop()

print(f"Compiled selector count: {len(compiled.selectors):,}")
print(f"Current memory: {current / 1024 / 1024:.1f} MB")
print(f"Peak memory: {peak / 1024 / 1024:.1f} MB")
print(f"Amplification ratio: {peak / len(selector):.0f}x")

##### Expected output:

##### Selector string size: 499,999 bytes (488 KB)
##### Compiled selector count: 250,000

##### Current memory: ~244 MB
##### Peak memory: ~244 MB

##### Amplification ratio: ~488x
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:

  • OOM kills in containerised deployments (Kubernetes pods, Docker containers) with memory limits
  • Swap thrashing on bare-metal servers, degrading performance for all co-located processes
  • Process termination via Python's MemoryError exception if the system runs out of addressable memory
Parameter Value
Input size ~500 KB selector string
Memory allocated ~244 MB
Amplification ratio ~488×
Per-object overhead ~976 bytes per selector
Authentication required None
User interaction required None

Scalability 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 Score: 7.5 / 10 (High)
  • Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Soup Sieve: Polynomial-time ReDoS (O(n²)) in the IDENTIFIER / VALUE selector sub-patterns

CVE-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 IDENTIFIER sub-pattern (also embedded in VALUE, 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 as a. 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's soup.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
##### lines 122-126
IDENTIFIER = fr'''
(?:(?:-?(?:[^\x00-\x2f\x30-\x40\x5B-\x5E\x60\x7B-\x9f]|{CSS_ESCAPES})+|--)
(?:[^\x00-\x2c\x2e\x2f\x3A-\x40\x5B-\x5E\x60\x7B-\x9f]|{CSS_ESCAPES})*)
'''

##### line 129 — VALUE embeds IDENTIFIER (so attribute values inherit the pattern)
VALUE = fr'''(?:"(?:\\(?:.|{NEWLINE})|[^\\"\r\n\f])*?"|'...'|{IDENTIFIER})'''
  • classA [^\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."
  • Both classes match ordinary letters (e.g. 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. IDENTIFIER matched greedily on "a"*n succeeds 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). Profiling compile("[a=" + "a"*4000) shows only 12 re.match calls 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)
  • git HEAD 751c57b (2.9, PYTHONPATH=src): cd src && python3 ../poc/poc_redos_compile.py.
  • Published PyPI soupsieve 2.8.4 (fresh uv 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).
  • Python 3.11.15 and 3.14.6 both reproduce.
PoC (poc/poc_redos_compile.py)
import sys, time
sys.path.insert(0, ".")
import soupsieve as sv

def compile_time(sel):
    t0 = time.perf_counter()
    try:
        sv.compile(sel)
        status = "ok"
    except Exception as e:
        status = type(e).__name__
    return (time.perf_counter() - t0), status

print(f"soupsieve {sv.__version__}\n")

print("Payload A: '[a=' + 'a'*n   (unterminated attribute value)")
for n in (1000, 2000, 4000, 8000):
    dt, st = compile_time("[a=" + "a" * n)
    print(f"  n={n:<6} len={3+n:<7} {dt*1000:9.1f} ms  [{st}]")

print("\nPayload B: 'a'*n + '!'   (identifier run + invalid trailing char)")
for n in (2000, 4000, 8000, 16000):
    dt, st = compile_time("a" * n + "!")
    print(f"  n={n:<6} len={n+1:<7} {dt*1000:9.1f} ms  [{st}]")

payload = "[a=" + "a" * 12000
dt, st = compile_time(payload)
print(f"\n[+] Single call: compile('[a=' + 'a'*12000)  (len={len(payload)})")
print(f"[+] wall time = {dt:.2f} s   [{st}]")

End-to-end note: bs4.BeautifulSoup(html).select(payload) reaches the same compile() 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)
soupsieve 2.9

Payload A: '[a=' + 'a'*n   (unterminated attribute value)
  n=1000   len=1003        214.8 ms  [SelectorSyntaxError]
  n=2000   len=2003        504.7 ms  [SelectorSyntaxError]
  n=4000   len=4003       2031.9 ms  [SelectorSyntaxError]
  n=8000   len=8003       8091.3 ms  [SelectorSyntaxError]

Payload B: 'a'*n + '!'   (identifier run + invalid trailing char)
  n=2000   len=2001         79.7 ms  [SelectorSyntaxError]
  n=4000   len=4001        322.9 ms  [SelectorSyntaxError]
  n=8000   len=8001       1328.9 ms  [SelectorSyntaxError]
  n=16000  len=16001      5379.4 ms  [SelectorSyntaxError]

[+] Single call: compile('[a=' + 'a'*12000)  (len=12003)
[+] wall time = 18.28 s   [SelectorSyntaxError]
Evidence — published 2.8.4 (verbatim poc/evidence_redos_compile_PUBLISHED_2.8.4.log)
soupsieve 2.8.4
Payload A: '[a=' + 'a'*n
  n=1000   len=1003        113.9 ms  [SelectorSyntaxError]
  n=2000   len=2003        457.2 ms  [SelectorSyntaxError]
  n=4000   len=4003       1816.8 ms  [SelectorSyntaxError]
  n=8000   len=8003       7299.0 ms  [SelectorSyntaxError]
[+] Single call: compile('[a=' + 'a'*12000)  wall time = 16.57 s   [SelectorSyntaxError]
Impact — calibrated
  • Confirmed: quadratic CPU consumption per 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).
  • Realistic exposure: services that accept user-supplied CSS selectors and feed them to BeautifulSoup/soupsieve.
  • NOT claimed: exponential blowup, memory corruption, or code execution. This is strictly an availability (DoS) issue, and only where selectors are attacker-influenced. Applications using only fixed selectors are unaffected — stated to avoid inflation.
Remediation
  • Remove the adjacent-quantifier ambiguity in 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.
  • Alternatively use atomic grouping / possessive quantifiers where supported ((?>...), *+) to forbid backtracking into the identifier run.
  • Defense-in-depth: cap selector length before compiling (reject selectors beyond a sane bound), since CSS selectors are realistically short.

Severity

  • CVSS Score: 5.3 / 10 (Medium)
  • Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L

References

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_iter trims 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(), the soupsieve.select/iselect/match/filter helpers, and BeautifulSoup's soup.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.py
##### line 185-186
RE_WS_BEGIN = re.compile(fr'^{WSC}*')   # anchored at start -> .search() only tries pos 0 -> linear (safe)
RE_WS_END   = re.compile(fr'{WSC}*$')   # NOT anchored at start -> .search() tries every offset

##### selector_iter, lines ~1322-1326
m = RE_WS_BEGIN.search(pattern)
index = m.end(0) if m else 0
m = RE_WS_END.search(pattern)                     # <-- O(n^2) here
end = (m.start(0) - 1) if m else (len(pattern) - 1)

WSC = (?:{WS}|{COMMENTS}). For RE_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_BEGIN avoids 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)
  • git HEAD 751c57b (2.9, PYTHONPATH=src): cd src && python3 ../poc/poc_redos_ws_trim.py.
  • Published PyPI soupsieve 2.8.4 (fresh uv 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).
  • Python 3.11.15 and 3.14.6 both reproduce.
PoC (poc/poc_redos_ws_trim.py)
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).__name__
    return time.perf_counter() - t0, st

print(f"soupsieve {sv.__version__}\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} {dt*1000: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 that the cost is in RE_WS_END.search specifically (poc/isolate_ws_trim.py): RE_WS_END on "div"+" "*n+">" is O(n²) (2000→100 ms, 4000→448 ms, 8000→1622 ms, 16000→6719 ms), while the start-anchored RE_WS_BEGIN on " "*n+"x" stays linear (32000→1.5 ms). Profiling compile shows the entire wall time in 2 re.Pattern.search calls, not .match.

Evidence — HEAD 2.9 (verbatim poc/evidence_redos_ws_trim.log)
soupsieve 2.9

VALID selector 'a' + ' '*n + 'b'  (descendant combinator, lots of whitespace):
  n=2000   len=2002        112.3 ms  [ok]
  n=4000   len=4002        411.5 ms  [ok]
  n=8000   len=8002       1602.9 ms  [ok]
  n=16000  len=16002      6464.1 ms  [ok]

VALID-looking 'a' + '/*x*/'*n + 'b'  (CSS comment run):
  n=1000   len=5002         48.9 ms  [SelectorSyntaxError]
  n=2000   len=10002       194.8 ms  [SelectorSyntaxError]
  n=4000   len=20002       780.2 ms  [SelectorSyntaxError]
  n=8000   len=40002      3145.3 ms  [SelectorSyntaxError]

[+] Single call: compile('a' + ' '*20000 + 'b')  (len=20002)
[+] wall time = 10.23 s   [ok]
Evidence — published 2.8.4 (verbatim poc/evidence_redos_ws_trim_PUBLISHED_2.8.4.log)
soupsieve 2.8.4

VALID selector 'a' + ' '*n + 'b':
  n=2000   len=2002        102.7 ms  [ok]
  n=4000   len=4002        404.3 ms  [ok]
  n=8000   len=8002       1618.2 ms  [ok]
  n=16000  len=16002      6457.9 ms  [ok]
[+] Single call: compile('a' + ' '*20000 + 'b')  wall time = 10.11 s   [ok]
Impact — calibrated
  • Confirmed: quadratic CPU per 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.
  • Realistic exposure: services that accept user-supplied CSS selectors and feed them to BeautifulSoup/soupsieve.
  • NOT claimed: exponential blowup, memory corruption, or code execution. Availability (DoS) only, and only where selectors are attacker-influenced.
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 in re.Pattern.search), whereas the IDENTIFIER/VALUE issue is adjacent-quantifier backtracking during token .match(). They can be fixed independently.

Remediation
  • Anchor or de-loop the trailing-trim step: instead of .search() of {WSC}*$, scan trailing whitespace/comments from the end directly (e.g. reverse scan, or re.compile(r'^{WSC}*').match on a reversed-equivalent), so no per-offset retry occurs.
  • Alternatively strip whitespace/comments in a single forward tokenizing pass rather than with a pre-pass *$ search.
  • Defense-in-depth: cap selector length before compiling.

Severity

  • CVSS Score: 5.3 / 10 (Medium)
  • Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Soup Sieve: General sibling combinator inside :has() matches untrusted HTML in quadratic time

CVE-2026-126192 / GHSA-4j63-6c4x-wfg4

More information

Details

Summary

Matching a selector that puts a general-sibling combinator inside :has(), such as div:has(a ~ b), against untrusted HTML runs in O(n²) time in the number of sibling elements. A single small document with a few thousand siblings ties the process up for a minute of CPU, so an application that runs a fixed selector of this shape over HTML it doesn't control can be stalled by one request.

:has is powerful, but much in the same way poor regular expressions can be crafted to cause catastrophic backtracking with greedy capturing, has can be used to create non-ideal scenarios that can cause large portions of the tree to be evaluated for one or many elements if not used thoughtfully.

This behavior is not exclusive to Soup Sieve, but browsers make note of these potential performance issues themselves: https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:has#performance_considerations.

With all of that said, there is room for improvements that can in some cases greatly improve performance.

Details

:has(a ~ b) asks "does this element contain an a that has a later sibling b?". When no b is present, soupsieve can't short-circuit, and it answers the question with a nested scan: match_future_child walks every candidate a under the scope, and for each one the general-sibling branch of match_future_relations walks all of that a's following siblings looking for b. With n siblings that is (n-1) + (n-2) + ... + 1 comparisons, i.e. O(n²).

The adjacent-sibling form div:has(a + b) stays linear, because its branch checks only the one immediately following sibling per candidate. So the cost is specific to the general-sibling ~ (and ~ nested inside :has), and it shows up on the common case where the right-hand side simply isn't there.

Proof of concept

Installed from PyPI: pip install soupsieve==2.9.2 beautifulsoup4.

import time
import soupsieve
from bs4 import BeautifulSoup

selector = "div:has(a ~ b)"

for n in (1000, 2000, 4000, 8000):
    html = "<div>" + ("<a></a>" * n) + "</div>"
    soup = BeautifulSoup(html, "html.parser")
    start = time.perf_counter()
    soupsieve.select(selector, soup)
    print(n, round((time.perf_counter() - start) * 1000), "ms")

Measured:

n=1000    837 ms
n=2000   3667 ms
n=4000  14797 ms
n=8000  59746 ms

Time quadruples for each doubling of n. The n=8000 input is a ~56 KB HTML fragment.

Impact

Availability. Any caller that evaluates a fixed selector containing a general sibling under :has() over attacker-controlled markup can be forced to spend minutes of CPU on a small input. The selector is developer-authored and ordinary; the untrusted part is the HTML, which is soupsieve's normal input.

A single-pass evaluation of the sibling run (remember whether an a has been seen while scanning the siblings once) would make this case linear.

Mitigation

Quadratic time for :has in some circumstances is expected and not a bug. While we've made optimizations and there may be more in the future, the way :has is designed to operate will never perform in linear time in all circumstances. Users should thoughtfully apply the use of :has.

The anchor selector (the A in A:has(B)) should not be an element that has too many children. Additionally, too general an anchor, such as *, can cause :has() to be applied to every element.

Further, it is recommended that the inner selector (the B in A:has(B)) should use combinators like > or + to limit traversal. When the selector inside :has() is not tightly constrained, Soup Sieve might need to traverse the entire subtree of the anchor
element to check the condition.

If the risk for a given project is too great, user input can be scanned for usage of :has and be denied. Calls can also be wrapped in a timeout to prevent long-running processes as well.

Severity

  • CVSS Score: 6.3 / 10 (Medium)
  • Vector String: CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Soup Sieve: Polynomial-time denial of service in the descendant combinator

CVE-2026-126190 / GHSA-mmhr-3c5m-f64f

More information

Details

Summary

Matching a selector that uses the descendant combinator (a b) takes time that grows polynomially with how deeply the document is nested, and each extra descendant combinator in the selector raises the degree by one. A selector with k descendant combinators takes O(n^(k+1)) time on a chain of n nested elements.

An attacker who controls the HTML or XML that an application passes to Soup Sieve (directly, or through Beautiful Soup's select(), select_one(), css.select(), etc.) can send a very small, deeply nested document that keeps a CPU busy for seconds to minutes against an ordinary selector such as section div div.

Details

To match a b, Soup Sieve checks each element against b and then walks up through its ancestors looking for one that matches a. When the selector has more descendant combinators, the rest of the selector is tried again from each ancestor on that walk, and so on for every combinator. Nothing was reused between walks, so the same ancestors were matched against the same part of the selector over and over.

The worst case is when the leftmost part of the selector never matches, as in section div div against a document of nested <div> with no <section>: every walk goes all the way to the root and no search can stop early.

Timings

Example timings with html.parser on Soup Sieve 3.0.4 and 4.0, using a document of nested <div> (5 bytes each) and no matches:

Nesting depth Input size Selector 3.0.4 4.0
2,000 10 KB section div 1.6 s 0.05 s
4,000 20 KB section div 5.1 s 0.01 s
400 2 KB section div div 6.7 s < 0.01 s
800 4 KB section div div 53.0 s < 0.01 s
100 0.5 KB section div div div 2.5 s < 0.01 s
200 1 KB section div div div 40.9 s < 0.01 s

None of these selectors use unusual syntax.

Proof of concept
import time
import bs4
import soupsieve as sv

soup = bs4.BeautifulSoup('<div>' * 200, 'html.parser')
t = time.process_time()
sv.select('section div div div', soup)
print(time.process_time() - t)

On affected versions, doubling the nesting depth multiplies the time by about 2^(k+1), where k is the number of descendant combinators: about 4x for section div, 8x for section div div, and 16x for section div div div.

Impact

Applications are affected if they run selectors with descendant combinators against documents that untrusted users can influence, such as scrapers, HTML sanitizers or filters, and services that process uploaded or fetched markup. Descendant combinators are among the most common parts of CSS selectors, and a document of about 1 KB is enough to tie up a worker for close to a minute. Applications that only run selectors against trusted documents are not affected.

Patches

Fixed in 4.0 by facelessuser/soupsieve#351. Whether an element is, or has an ancestor that is, a match for part of a selector depends only on its parent, so Soup Sieve now caches that result per element and relation for every element visited on the walk. Each descendant combinator is then linear in the size of the document. Results are unchanged, and ordinary documents are no slower.

Caching is enabled by default, so no changes are needed beyond upgrading. The NOCACHE flag is a temporary fallback for users who run into problems with caching, and it is planned for removal. It uses the old uncached matching, so selectors compiled with NOCACHE are still subject to polynomial time and shouldn't be used with descendant combinators on untrusted input.

Workarounds

If users can't upgrade:

  • Use as few descendant combinators as they can in selectors that run on untrusted documents. Where the structure of the
    document is known, the child combinator (a > b) only looks at the parent and is not affected.
  • Limit the size and nesting depth of untrusted documents before selecting on them.
  • Run selection on untrusted input with a time limit, for example in a worker process that can be stopped.

Severity

  • CVSS Score: 7.5 / 10 (High)
  • Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Soup Sieve: Uncontrolled recursion in CSS parser lets nested pseudo-class selectors exhaust the Python call stack

CVE-2026-126193 / GHSA-7g44-g9rc-x53w

More information

Details

Summary

The Soup Sieve CSS selector parser recurses once per level of nested pseudo-class selector (:is(), :not(), :where(), :has()) without any limit on nesting depth. An unauthenticated attacker who can supply a CSS selector string to soupsieve.compile(), soupsieve.select(), or Beautiful Soup's soup.select() / soup.select_one() can force the parser to exceed Python's recursion limit with a small crafted input (about 1.6 KB, roughly 330 nesting levels), raising an uncontrolled RecursionError. Because this exception is not part of the parser's documented error contract, applications that catch only ValueError or SelectorSyntaxError see an unexpected failure of the request or task that parsed the selector.

Root Cause

The parser dispatches each opening pseudo-class token to parse_pseudo_open, which calls back into parse_selectors to parse the pseudo-class body. Every nesting level therefore adds Python stack frames, and no counter, depth guard, or RecursionError handler bounds this descent. The token-count guard SELECTOR_LIMIT = 8192 (added for the earlier comma-separated-list memory issue) counts parsed tokens, not recursion depth; at the failing nesting depth the token count is only about 330, roughly four percent of that limit, so the guard never fires. The security invariant that untrusted selector input must not be able to exhaust interpreter resources is not enforced for nesting depth.

soupsieve/css_parser.py
  CSSParser.parse_pseudo_open (line 985): sel.selectors.append(self.parse_selectors(iselector, index, flags))
  CSSParser.parse_selectors  (line 1137): recursive target; dispatches pseudo-class tokens back into parse_pseudo_class -> parse_pseudo_open
  CSSParser.increment_count  (lines 707-712): SELECTOR_LIMIT token guard; increments once per nesting level, cannot bound depth
Impact

Any application that compiles or evaluates untrusted CSS selectors through Soup Sieve (directly or via Beautiful Soup) is affected. No authentication, privilege, or user interaction is required; the failure occurs during selector parsing, before any document is matched. A single crafted selector of about 1.6 KB reliably raises RecursionError. The demonstrated capability is a transient denial of the individual request, task, or invocation that parsed the malicious selector: the exception propagates uncaught unless the caller handles RecursionError (or a broad except Exception). The RecursionError in CPython 3.x is a recoverable stack unwind, not an interpreter crash or persistent resource exhaustion, and it carries no confidentiality or integrity impact. Applications following the parser's documented error pattern (catching ValueError / SelectorSyntaxError) will not catch it, so a repeated stream of such selectors can plausibly cause repeated per-request failures; this per-request availability effect is the demonstrated consequence, not a service-wide or amplified outage.

Proof of Concept

Requires only an installed Soup Sieve (any release from 0.4 through 2.10) and a default CPython recursion limit of 1000. No Beautiful Soup document or network setup is needed.

import soupsieve as sv

##### ~330 levels of nested :is() around a single tag: about 1.6 KB of input.
depth = 330
selector = ":is(" * depth + "a" + ")" * depth
print("selector length:", len(selector))
sv.compile(selector)
selector length: 1651
Traceback (most recent call last):
  ...
  File ".../soupsieve/css_parser.py", line 985, in parse_pseudo_open
    sel.selectors.append(self.parse_selectors(iselector, index, flags))
RecursionError: maximum recursion depth exceeded

The same failure was reproduced with :not(), :where(), and :has(), and through BeautifulSoup(...).select(selector). A negative control at depth 100 compiles successfully with a token count of 101, confirming the token guard is far from firing.

Attack Chain
  1. Exposure: The application exposes soupsieve.compile() / select() or Beautiful Soup's .select() / .select_one() to attacker-influenced CSS selector input. These are the library's primary public API and ship enabled by default; Soup Sieve is an automatic dependency of Beautiful Soup 4.
  2. Control: The attacker fully controls the selector string. The nested pseudo-class pattern (:is(:is(...a...))) survives tokenization and normalization unchanged.
  3. Path: compile() reaches CSSParser.process_selectors() and then parse_selectors(), which dispatches each pseudo-class token through parse_pseudo_class() into parse_pseudo_open(), which calls parse_selectors() again, once per nesting level.
  4. Guard: The only resource guard is the SELECTOR_LIMIT token counter, which reaches about 330 at the failing depth versus its limit of 8192, so it never fires. No recursion-depth counter and no RecursionError handler exist anywhere in the package.
  5. Primitive: Python's recursion limit is exceeded during parsing, raising an uncontrolled RecursionError.
  6. Result: The unhandled RecursionError propagates to the caller, failing the request, task, or process invocation that parsed the selector, outside the parser's documented ValueError / SelectorSyntaxError error contract.
Bypass Evidence

The relevant existing guard is the SELECTOR_LIMIT = 8192 token counter in increment_count(). It does not block this path: it increments once per nesting level, so at the failing depth of about 330 the count is roughly 330 (about four percent of 8192), verified by instrumenting increment_count and observing a maximum count of 329 at the point RecursionError was raised. A search of the package for RecursionError and RuntimeError handlers returns no matches, so nothing catches the error internally, and functools.lru_cache on the compile path does not cache exceptions. Attempts to invalidate the finding were made and failed: raising the interpreter recursion limit only shifts the required nesting depth (a proportionally larger but still small selector), and a broad except Exception in the caller does catch the error but the parser's documented contract raises only ValueError / SelectorSyntaxError, so callers coded to that contract remain exposed. This is a distinct weakness from the prior comma-separated-list memory-exhaustion advisory: that issue exhausts heap memory through linear object expansion and was addressed by the token count limit, whereas this issue exhausts the call stack through unbounded recursion depth and is not addressed by any released version, including 2.8.4 which was independently confirmed to still raise RecursionError on this input.

Affected Versions
  • Ecosystem: pip (PyPI)
  • Package: soupsieve
  • Confirmed affected range: >= 0.4, <= 2.10
  • Latest release checked: soupsieve 2.10 (PyPI, latest published release as of 2026-09-25)
  • Fix status: not fixed in any released version

The recursive parse_pseudo_open to parse_selectors structure is present in the earliest PyPI release (0.4) and in every subsequent release through the current 2.10, verified against the published PyPI source artifacts. Version 2.5, release 2.8.4 (which added that token-count limit), and 2.10 were each executed and confirmed to raise RecursionError on the crafted input. No released version adds a recursion-depth limit; the token-count SELECTOR_LIMIT introduced in 2.8.4 is architecturally unable to bound nesting depth. There is no registry or tag disagreement and no package or module path change.

Possible Solution

Python's RecursionError is sometimes a contested issue related to security. The Soup Sieve project does not consider it "uncontrolled" and allows it to be raised to indicate Python's maximum recursion depth was reached, and allows it to fail to prevent overflows, a much worse and real security vulnerability. With that said, the algorithm can be reworked to be non-recursive, allowing the selector limit check to now capture and raise an exception. In addition, the limit could be exposed to the user to give them better control.

Mitigation

For those on older versions where the algorithm is still recursive, a try/except block that captures RecursionError can be added around calls if accepting untrusted user input is allowed.

Note

This issue was fixed in version 3.0.0, which has been yanked from the PyPI registry. Use versions 3.0.2 and above to upgrade.


Reported by zx (GitHub: @​manus-pi).

Severity

  • CVSS Score: 5.3 / 10 (Medium)
  • Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L

References

[!IMPORTANT]
✂ PR body was truncated to here.

@renovate

renovate Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

Branch automerge failure

This PR was configured for branch automerge. However, this is not possible, so it has been raised as a PR instead.


  • Branch has one or more failed status checks

@github-actions github-actions Bot added the docs label Jul 11, 2026
@socket-security

socket-security Bot commented Jul 11, 2026 •

Copy link
Copy Markdown

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.

@renovate
renovate Bot force-pushed the renovate/pypi-soupsieve-vulnerability branch from e944e2d to 5a37a50 Compare July 13, 2026 05:51

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 👍

@renovate
renovate Bot force-pushed the renovate/pypi-soupsieve-vulnerability branch from 5a37a50 to c949498 Compare July 27, 2026 02:39

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 👍

@renovate
renovate Bot force-pushed the renovate/pypi-soupsieve-vulnerability branch from c949498 to bd337be Compare August 3, 2026 04:56

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 👍

@renovate
renovate Bot force-pushed the renovate/pypi-soupsieve-vulnerability branch from bd337be to dc834c2 Compare August 10, 2026 00:43

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 👍

@renovate
renovate Bot force-pushed the renovate/pypi-soupsieve-vulnerability branch 5 times, most recently from 3a4fb3b to 6e65c08 Compare August 14, 2026 04:40

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 👍

@renovate
renovate Bot force-pushed the renovate/pypi-soupsieve-vulnerability branch from 6e65c08 to b567edf Compare August 17, 2026 01:01

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 👍

@renovate
renovate Bot force-pushed the renovate/pypi-soupsieve-vulnerability branch 8 times, most recently from bda35c0 to ebacf39 Compare August 24, 2026 02:06
@renovate
renovate Bot force-pushed the renovate/pypi-soupsieve-vulnerability branch from ebacf39 to d2966aa Compare August 24, 2026 06:16
@renovate
renovate Bot force-pushed the renovate/pypi-soupsieve-vulnerability branch from a2c9601 to 72ac5bb Compare October 3, 2026 05:33

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scan Summary

Tool Critical High Medium Low Status
Dependency Scan (universal) 2 12 9 1 ❌
Security Audit for Infrastructure 14 92 8 32 ❌
Secrets Audit 0 4 0 0 ❌
Kotlin Static Analysis 0 0 0 0 ✅
Shell Script Analysis 0 0 0 195 ✅
Python Source Analyzer 0 0 0 0 ✅
Kotlin Security Audit 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 👍

@renovate
renovate Bot force-pushed the renovate/pypi-soupsieve-vulnerability branch 5 times, most recently from 1cd4a81 to 54b5f50 Compare October 6, 2026 09:07

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scan Summary

Tool Critical High Medium Low Status
Dependency Scan (universal) 2 7 8 1 ❌
Security Audit for Infrastructure 14 92 8 32 ❌
Secrets Audit 0 4 0 0 ❌
Kotlin Static Analysis 0 0 0 0 ✅
Shell Script Analysis 0 0 0 195 ✅
Python Source Analyzer 0 0 0 0 ✅
Kotlin Security Audit 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 👍

@renovate
renovate Bot force-pushed the renovate/pypi-soupsieve-vulnerability branch 2 times, most recently from a007392 to e4965ae Compare October 8, 2026 11:43

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scan Summary

Tool Critical High Medium Low Status
Dependency Scan (universal) 2 7 7 1 ❌
Security Audit for Infrastructure 14 92 8 32 ❌
Secrets Audit 0 4 0 0 ❌
Kotlin Static Analysis 0 0 0 0 ✅
Shell Script Analysis 0 0 0 195 ✅
Python Source Analyzer 0 0 0 0 ✅
Kotlin Security Audit 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 👍

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scan Summary

Tool Critical High Medium Low Status
Dependency Scan (universal) 2 6 7 0 ❌
Security Audit for Infrastructure 14 92 8 32 ❌
Secrets Audit 0 4 0 0 ❌
Kotlin Static Analysis 0 0 0 0 ✅
Shell Script Analysis 0 0 0 195 ✅
Python Source Analyzer 0 0 0 0 ✅
Kotlin Security Audit 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 👍

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scan Summary

Tool Critical High Medium Low Status
Dependency Scan (universal) 2 7 8 0 ❌
Security Audit for Infrastructure 14 92 8 32 ❌
Secrets Audit 0 4 0 0 ❌
Kotlin Static Analysis 0 0 0 0 ✅
Shell Script Analysis 0 0 0 195 ✅
Python Source Analyzer 0 0 0 0 ✅
Kotlin Security Audit 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 👍

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scan Summary

Tool Critical High Medium Low Status
Dependency Scan (universal) 3 7 8 0 ❌
Security Audit for Infrastructure 14 92 8 32 ❌
Secrets Audit 0 4 0 0 ❌
Kotlin Static Analysis 0 0 0 0 ✅
Shell Script Analysis 0 0 0 195 ✅
Python Source Analyzer 0 0 0 0 ✅
Kotlin Security Audit 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 👍

@github-actions

Copy link
Copy Markdown
Contributor

Welcome, new contributor!

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scan Summary

Tool Critical High Medium Low Status
Dependency Scan (universal) 3 11 10 0 ❌
Python Source Analyzer 0 0 0 0 ✅
Kotlin Security Audit 0 0 0 0 ✅
Shell Script Analysis 0 0 0 195 ✅
Kotlin Static Analysis 0 0 0 0 ✅
Secrets Audit 0 4 0 0 ❌
Security Audit for Infrastructure 14 92 8 32 ❌

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 👍

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scan Summary

Tool Critical High Medium Low Status
Dependency Scan (universal) 3 13 10 0 ❌
Kotlin Security Audit 0 0 0 0 ✅
Kotlin Static Analysis 0 0 0 0 ✅
Python Source Analyzer 0 0 0 0 ✅
Shell Script Analysis 0 0 0 195 ✅
Secrets Audit 0 4 0 0 ❌
Security Audit for Infrastructure 14 92 8 32 ❌

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 👍

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scan Summary

Tool Critical High Medium Low Status
Dependency Scan (universal) 3 13 11 0 ❌
Python Source Analyzer 0 0 0 0 ✅
Kotlin Security Audit 0 0 0 0 ✅
Shell Script Analysis 0 0 0 195 ✅
Kotlin Static Analysis 0 0 0 0 ✅
Secrets Audit 0 4 0 0 ❌
Security Audit for Infrastructure 14 92 8 32 ❌

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 👍

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scan Summary

Tool Critical High Medium Low Status
Dependency Scan (universal) 2 12 11 4 ❌
Python Source Analyzer 0 0 0 0 ✅
Kotlin Security Audit 0 0 0 0 ✅
Shell Script Analysis 0 0 0 195 ✅
Kotlin Static Analysis 0 0 0 0 ✅
Secrets Audit 0 4 0 0 ❌
Security Audit for Infrastructure 14 92 8 32 ❌

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 👍

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scan Summary

Tool Critical High Medium Low Status
Dependency Scan (universal) 2 12 11 4 ❌
Python Source Analyzer 0 0 0 0 ✅
Kotlin Security Audit 0 0 0 0 ✅
Shell Script Analysis 0 0 0 195 ✅
Kotlin Static Analysis 0 0 0 0 ✅
Secrets Audit 0 5 0 0 ❌
Security Audit for Infrastructure 14 92 8 32 ❌

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 👍

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scan Summary

Tool Critical High Medium Low Status
Dependency Scan (universal) 3 10 9 4 ❌
Python Source Analyzer 0 0 0 0 ✅
Kotlin Security Audit 0 0 0 0 ✅
Shell Script Analysis 0 0 0 195 ✅
Kotlin Static Analysis 0 0 0 0 ✅
Secrets Audit 0 4 0 0 ❌
Security Audit for Infrastructure 14 92 8 32 ❌

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 👍

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scan Summary

Tool Critical High Medium Low Status
Dependency Scan (universal) 2 9 10 1 ❌
Kotlin Security Audit 0 0 0 0 ✅
Kotlin Static Analysis 0 0 0 0 ✅
Python Source Analyzer 0 0 0 0 ✅
Shell Script Analysis 0 0 0 195 ✅
Secrets Audit 0 4 0 0 ❌
Security Audit for Infrastructure 14 92 8 32 ❌

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 👍

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scan Summary

Tool Critical High Medium Low Status
Dependency Scan (universal) 2 6 8 0 ❌
Kotlin Security Audit 0 0 0 0 ✅
Kotlin Static Analysis 0 0 0 0 ✅
Python Source Analyzer 0 0 0 0 ✅
Shell Script Analysis 0 0 0 195 ✅
Secrets Audit 0 4 0 0 ❌
Security Audit for Infrastructure 14 92 8 32 ❌

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 👍

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scan Summary

Tool Critical High Medium Low Status
Dependency Scan (universal) 2 7 7 0 ❌
Kotlin Security Audit 0 0 0 0 ✅
Kotlin Static Analysis 0 0 0 0 ✅
Python Source Analyzer 0 0 0 0 ✅
Shell Script Analysis 0 0 0 195 ✅
Secrets Audit 0 4 0 0 ❌
Security Audit for Infrastructure 14 92 8 32 ❌

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 👍

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scan Summary

Tool Critical High Medium Low Status
Dependency Scan (universal) 2 7 7 1 ❌
Security Audit for Infrastructure 14 92 8 32 ❌
Kotlin Static Analysis 0 0 0 0 ✅
Python Source Analyzer 0 0 0 0 ✅
Secrets Audit 0 4 0 0 ❌
Shell Script Analysis 0 0 0 195 ✅
Kotlin Security Audit 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 👍

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scan Summary

Tool Critical High Medium Low Status
Dependency Scan (universal) 2 8 7 3 ❌
Security Audit for Infrastructure 14 92 8 32 ❌
Kotlin Static Analysis 0 0 0 0 ✅
Python Source Analyzer 0 0 0 0 ✅
Secrets Audit 0 4 0 0 ❌
Shell Script Analysis 0 0 0 195 ✅
Kotlin Security Audit 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 👍

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scan Summary

Tool Critical High Medium Low Status
Dependency Scan (universal) 2 14 12 3 ❌
Security Audit for Infrastructure 14 92 8 32 ❌
Kotlin Static Analysis 0 0 0 0 ✅
Python Source Analyzer 0 0 0 0 ✅
Secrets Audit 0 4 0 0 ❌
Shell Script Analysis 0 0 0 195 ✅
Kotlin Security Audit 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 👍

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scan Summary

Tool Critical High Medium Low Status
Dependency Scan (universal) 2 15 12 3 ❌
Security Audit for Infrastructure 14 92 8 32 ❌
Kotlin Static Analysis 0 0 0 0 ✅
Python Source Analyzer 0 0 0 0 ✅
Secrets Audit 0 4 0 0 ❌
Shell Script Analysis 0 0 0 195 ✅
Kotlin Security Audit 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>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scan Summary

Tool Critical High Medium Low Status
Dependency Scan (universal) 2 16 13 3 ❌
Security Audit for Infrastructure 14 92 8 32 ❌
Kotlin Static Analysis 0 0 0 0 ✅
Python Source Analyzer 0 0 0 0 ✅
Secrets Audit 0 4 0 0 ❌
Shell Script Analysis 0 0 0 195 ✅
Kotlin Security Audit 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 branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants