
Cloud Vulnerability DB
A community-led vulnerabilities database
Some ordinary uses of :has() take time that grows quadratically with the size of the document:
:has() with a descendant relative selector, such as div:has(p) or :has(em), is quadratic in how deeply the
document is nested.:has() with a child relative selector, used on the parent side of a child combinator, such as
div:has(> p) > span, is quadratic in how many children an element has.
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 small document, either deeply nested or with many
sibling elements, that keeps a CPU busy for seconds to minutes against a selector written by the application.:has()For each candidate element, :has() with a descendant relation searched the candidate's whole subtree for a match.
Nothing was reused between candidates. In a chain of n nested elements, each one is a candidate, and the subtree
below each is searched again, so matching takes O(n²) time. The worst case is when nothing matches, because no
search can stop early. The same happens when the only match is at the very bottom of the chain.
:has() on the parent side of a child combinatorTo match a > b, Soup Sieve checks each b and then checks whether its parent matches a. The result for the parent
was not reused, so a parent with n children was matched against a once per child. When a contains :has(> x),
each of those checks looks at all n children again, so matching takes O(n²) time.
Example timings with html.parser, before and after the fix:
| Document | Selector | Matches | Before | After |
|---|---|---|---|---|
4,000 nested <div> (20 KB) | div:has(p) | 0 | 5.1 s | 0.07 s |
8,000 nested <div> (40 KB) | div:has(p) | 0 | 17.9 s | 0.04 s |
4,000 nested <div>, <span> at the bottom | div:has(span) | 4,000 | 5.6 s | 0.02 s |
<div> with 5,000 <span> children (65 KB) | div:has(> p) > span | 0 | 12.0 s | 0.06 s |
<div> with 10,000 <span> children (130 KB) | div:has(> p) > span | 0 | 49.1 s | 0.03 s |
| None of these selectors use unusual syntax, and all of them are allowed in the default (strict) mode. |
import time
import bs4
import soupsieve as sv
cases = [
('<div>' * 8000, 'div:has(p)'),
('<div>' + '<span></span>' * 5000 + '</div>', 'div:has(> p) > span'),
]
for markup, selector in cases:
soup = bs4.BeautifulSoup(markup, 'html.parser')
t = time.process_time()
sv.select(selector, soup)
print(selector, time.process_time() - t)On affected versions, doubling the nesting depth (first case) or the number of children (second case) roughly quadruples the time.
Applications are affected if they run selectors like the ones above against documents that untrusted users can influence, such as scrapers, HTML sanitizers or filters, and services that process uploaded or fetched markup. A single request can tie up a worker for a long time. Applications that only run such selectors against trusted documents are not affected.
Fixed in 4.0.1 by:
:has() with a
descendant relation, Soup Sieve now caches, per element and relation, whether an element is or contains a match. A
subtree that has been searched once is not searched again, which makes the match linear in the size of the document.305fbf6: when matching the child combinator, whether the parent matches is now cached, so it is matched once
for all of its children instead of once per child.
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 quadratic time and shouldn't be used with :has() on
untrusted input.If you can't upgrade:
:has() with a descendant relative selector (:has(x)) on untrusted documents, and avoid putting
:has() on the parent side of a child combinator (a:has(> x) > b).:has: https://facelessuser.github.io/soupsieve/api/?h=ignore#ignore-pseudo-class.0469c14)305fbf6Source: NVD
Free Vulnerability Assessment
Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.
Get a personalized demo
"Best User Experience I have ever seen, provides full visibility to cloud workloads."
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
"We know that if Wiz identifies something as critical, it actually is."