Repository navigation
chore(deps): update dependency soupsieve to v4 [security] - #782
Open
renovate[bot] wants to merge 1 commit into
Open
renovate[bot] wants to merge 1 commit into
renovate[bot] wants to merge 1 commit into
Conversation
|
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Note
This PR body was truncated due to platform limits.
Important
Security fix, opened without the 5-day wait
Renovate opened this PR because a published advisory affects the current version. It skipped the minimum release age (
minimumReleaseAge) check, so the fixed version might be younger than 5 days. Read the advisory below, check the release age and the upstream release, then merge by hand.This exception is configured in renovate.json.
This PR contains the following updates:
2.10→4.0Soup 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 withkdescendant combinators takesO(n^(k+1))time on a chain ofnnested 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 assection div div.Details
To match
a b, Soup Sieve checks each element againstband then walks up through its ancestors looking for one that matchesa. 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 divagainst 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.parseron Soup Sieve 3.0.4 and 4.0, using a document of nested<div>(5 bytes each) and no matches:section divsection divsection div divsection div divsection div div divsection div div divNone of these selectors use unusual syntax.
Proof of concept
On affected versions, doubling the nesting depth multiplies the time by about 2^(k+1), where
kis the number of descendant combinators: about 4x forsection div, 8x forsection div div, and 16x forsection 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
NOCACHEflag 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 withNOCACHEare still subject to polynomial time and shouldn't be used with descendant combinators on untrusted input.Workarounds
If users can't upgrade:
document is known, the child combinator (
a > b) only looks at the parent and is not affected.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: 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 tosoupsieve.compile(),soupsieve.select(), or Beautiful Soup'ssoup.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 uncontrolledRecursionError. Because this exception is not part of the parser's documented error contract, applications that catch onlyValueErrororSelectorSyntaxErrorsee 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 intoparse_selectorsto parse the pseudo-class body. Every nesting level therefore adds Python stack frames, and no counter, depth guard, orRecursionErrorhandler bounds this descent. The token-count guardSELECTOR_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.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 handlesRecursionError(or a broadexcept Exception). TheRecursionErrorin 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 (catchingValueError/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.
The same failure was reproduced with
:not(),:where(), and:has(), and throughBeautifulSoup(...).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
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.:is(:is(...a...))) survives tokenization and normalization unchanged.compile()reachesCSSParser.process_selectors()and thenparse_selectors(), which dispatches each pseudo-class token throughparse_pseudo_class()intoparse_pseudo_open(), which callsparse_selectors()again, once per nesting level.SELECTOR_LIMITtoken counter, which reaches about 330 at the failing depth versus its limit of 8192, so it never fires. No recursion-depth counter and noRecursionErrorhandler exist anywhere in the package.RecursionError.RecursionErrorpropagates to the caller, failing the request, task, or process invocation that parsed the selector, outside the parser's documentedValueError/SelectorSyntaxErrorerror contract.Bypass Evidence
The relevant existing guard is the
SELECTOR_LIMIT = 8192token counter inincrement_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 instrumentingincrement_countand observing a maximum count of 329 at the pointRecursionErrorwas raised. A search of the package forRecursionErrorandRuntimeErrorhandlers returns no matches, so nothing catches the error internally, andfunctools.lru_cacheon 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 broadexcept Exceptionin the caller does catch the error but the parser's documented contract raises onlyValueError/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 raiseRecursionErroron this input.Affected Versions
Ecosystem: pip (PyPI)Package: soupsieveConfirmed affected range: >= 0.4, <= 2.10Latest release checked: soupsieve 2.10 (PyPI, latest published release as of 2026-09-25)Fix status: not fixed in any released versionThe recursive
parse_pseudo_opentoparse_selectorsstructure 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 raiseRecursionErroron the crafted input. No released version adds a recursion-depth limit; the token-countSELECTOR_LIMITintroduced 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
RecursionErroris 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
RecursionErrorcan 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: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: Exponential ReDoS in selector compilation via \9-run in CSS_ESCAPES/IDENTIFIER
CVE-2026-126191 / GHSA-6655-6qjp-jx7x
More information
Details
Summary
The IDENTIFIER/CSS_ESCAPES regex (css_parser.py:119-124) exhibits exponential backtracking on a run of ambiguous digit-escapes (
\9) in an attribute-name or tag position of an otherwise well-formed selector. It fires at compile time (before the SELECTOR_LIMIT guard) via the publicsoupsieve.compile()and BeautifulSoup.select().PoC (verified on soupsieve 2.9.2)
Novelty
Distinct from the patched GHSA-836r-... (VALUE regex) and GHSA-2wc2-... (comma-list) advisories, which are fixed in 2.9.2; this is a different regex/vector.
Reachability
CSS selectors are usually developer-controlled, so exposure requires an application that compiles UNTRUSTED selector strings (web-scraping-as-a-service, no-code/query builders, user-defined extraction rules). Where selectors are user-supplied, a ~60-byte string hangs the worker.
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.
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: Exponential ReDoS in selector compilation via \9-run in CSS_ESCAPES/IDENTIFIER
CVE-2026-126191 / GHSA-6655-6qjp-jx7x
More information
Details
Summary
The IDENTIFIER/CSS_ESCAPES regex (css_parser.py:119-124) exhibits exponential backtracking on a run of ambiguous digit-escapes (
\9) in an attribute-name or tag position of an otherwise well-formed selector. It fires at compile time (before the SELECTOR_LIMIT guard) via the publicsoupsieve.compile()and BeautifulSoup.select().PoC (verified on soupsieve 2.9.2)
Novelty
Distinct from the patched GHSA-836r-... (VALUE regex) and GHSA-2wc2-... (comma-list) advisories, which are fixed in 2.9.2; this is a different regex/vector.
Reachability
CSS selectors are usually developer-controlled, so exposure requires an application that compiles UNTRUSTED selector strings (web-scraping-as-a-service, no-code/query builders, user-defined extraction rules). Where selectors are user-supplied, a ~60-byte string hangs the worker.
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.
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 OSV and 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 tosoupsieve.compile(),soupsieve.select(), or Beautiful Soup'ssoup.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 uncontrolledRecursionError. Because this exception is not part of the parser's documented error contract, applications that catch onlyValueErrororSelectorSyntaxErrorsee 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 intoparse_selectorsto parse the pseudo-class body. Every nesting level therefore adds Python stack frames, and no counter, depth guard, orRecursionErrorhandler bounds this descent. The token-count guardSELECTOR_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.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 handlesRecursionError(or a broadexcept Exception). TheRecursionErrorin 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 (catchingValueError/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.
The same failure was reproduced with
:not(),:where(), and:has(), and throughBeautifulSoup(...).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
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.:is(:is(...a...))) survives tokenization and normalization unchanged.compile()reachesCSSParser.process_selectors()and thenparse_selectors(), which dispatches each pseudo-class token throughparse_pseudo_class()intoparse_pseudo_open(), which callsparse_selectors()again, once per nesting level.SELECTOR_LIMITtoken counter, which reaches about 330 at the failing depth versus its limit of 8192, so it never fires. No recursion-depth counter and noRecursionErrorhandler exist anywhere in the package.RecursionError.RecursionErrorpropagates to the caller, failing the request, task, or process invocation that parsed the selector, outside the parser's documentedValueError/SelectorSyntaxErrorerror contract.Bypass Evidence
The relevant existing guard is the
SELECTOR_LIMIT = 8192token counter inincrement_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 instrumentingincrement_countand observing a maximum count of 329 at the pointRecursionErrorwas raised. A search of the package forRecursionErrorandRuntimeErrorhandlers returns no matches, so nothing catches the error internally, andfunctools.lru_cacheon 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 broadexcept Exceptionin the caller does catch the error but the parser's documented contract raises onlyValueError/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 raiseRecursionErroron this input.Affected Versions
Ecosystem: pip (PyPI)Package: soupsieveConfirmed affected range: >= 0.4, <= 2.10Latest release checked: soupsieve 2.10 (PyPI, latest published release as of 2026-09-25)Fix status: not fixed in any released versionThe recursive
parse_pseudo_opentoparse_selectorsstructure 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 raiseRecursionErroron the crafted input. No released version adds a recursion-depth limit; the token-countSELECTOR_LIMITintroduced 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
RecursionErroris 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
RecursionErrorcan 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:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:LReferences
This data is provided by OSV and 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 withkdescendant combinators takesO(n^(k+1))time on a chain ofnnested 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 assection div div.Details
To match
a b, Soup Sieve checks each element againstband then walks up through its ancestors looking for one that matchesa. 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 divagainst 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.parseron Soup Sieve 3.0.4 and 4.0, using a document of nested<div>(5 bytes each) and no matches:section divsection divsection div divsection div divsection div div divsection div div divNone of these selectors use unusual syntax.
Proof of concept
On affected versions, doubling the nesting depth multiplies the time by about 2^(k+1), where
kis the number of descendant combinators: about 4x forsection div, 8x forsection div div, and 16x forsection 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
NOCACHEflag 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 withNOCACHEare still subject to polynomial time and shouldn't be used with descendant combinators on untrusted input.Workarounds
If users can't upgrade:
document is known, the child combinator (
a > b) only looks at the parent and is not affected.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 OSV and the GitHub Advisory Database (CC-BY 4.0).
Release Notes
facelessuser/soupsieve (soupsieve)
v4.0Compare Source
4.0
templateare no longer treated as part of the document. Selectors do notdescend into a
template, and elements inside one can't see beyond it, which also applies to pseudo-classes suchas
:lang(),:disabled, and:default.selectandfilterwill still reach the contents of atemplatewhenthey are called on the
templateitself, for examplesoup.template.select('p'). See theFAQ for more information.
iframeare no longer treated as a separate document, as they are not one in abrowser. Elements in an
iframeare normal children, so:rootdoes not match them, and:default,:indeterminate,:checked,:dir(),:lang(), and:read-writetreat them like any other element. Recentversions of
html.parser,lxml, andhtml5liball escape the contents of aniframelike a browser does, sothis mostly affects XHTML, Python versions that still don't escape such content in their specific
html.parser,and elements added to an
iframeby hand.:-soup-contains()now also includes the escaped text in aniframe.See the FAQ for more information.
:disabledand:enabledto treatoptionandoptgroupelements inside a disabledselectasdisabled, following a recent HTML specification update.
:has(~ ...)never matching top level elements of a fragmentthat has no root element, unless caching was disabled.
:indeterminatenow treats a radio button group without a form as extending across the whole documentinstead of the top level element that contains it, and it no longer ignores radio buttons at the top level.
:defaultto treat abuttonwith a missing or invalidtype, and aninputwithtype="image",as submit buttons.
:in-rangeand:out-of-rangeraisingTypeErrorwhen the document contains aninputwithout atypeattribute.:read-write(and:read-only) now treatcontenteditable="plaintext-only"elements as editing hosts andmatch the editable elements inside an editing host, following the nearest
contenteditablestate.:read-write(and:read-only) now ignorecontenteditableon non-HTML elements, such as an SVGforeignObject, as the attribute only applies to HTML elements. Such elements inherit the state of their parent.:checkednow only matches the last radio button with acheckedattribute in a radio button group, asin a browser, where a group is the radio buttons with the same
nameand form, including theformattribute.:defaultstill matches all of them, and the contents of atemplateare unaffected as they are not connected.:dir()and:definedno longer affect the rest of the selector list they are in. Previously, a selectorlist that used either of them could not have its combinators cross an
iframe, ignored the specified namespaces,and matched nothing in XML, not even the parts that didn't use them. They still do not match in XML, but
:not(:dir(ltr))and:not(:defined)now do.:defaultand:indeterminatenow honor theformattribute, which can associate a submit button orradio button with a form it is not in, or leave it without a form. It is ignored in a
template, as in the HTMLspecification. Submit buttons outside of a form can now be its default button.
:default,:checked, and:indeterminatenow handle a form inside a form like a browser. In HTML, abrowser ignores the nested form and takes its end tag for that of the parent, which matters for parsers that keep
the nested form, such as
html.parser. In XHTML and in atemplate, nested forms are real forms.:checkednow matches theoptionthat a browser would select. Withoutmultiple, that is only thelast
optionwith aselectedattribute in aselect, and if there are none and theselectshows a singleoption, the first
optionthat is not disabled.:defaultstill only considers theselectedattribute.[attribute!=value]excluding values that only start with the specified value, and in XML documents,typevalues that contain a newline.rel,method,lang,dir, ...) without ASCII case sensitivity on HTML elements in HTML documents, not justtype.Non-HTML elements are matched case sensitively.
:dir()now matches non-HTML elements, such as SVG and MathML, by the direction they inherit from theirparent (the
dirattribute only applies to HTML elements), and HTML elements inside them, such as in an SVGforeignObject, by the direction of their ancestors.:dir()ignoring the value of aninputwithdir="auto"and a missing or emptytype.:dir()now uses a table generated from Unicode 18 instead ofunicodedata, so results no longer dependon the Unicode version of the Python in use, and unassigned code points use their default direction.
:dir()now uses the value of aninputwithdir="auto"whentypeishidden,password,submit,reset, orbutton, as the HTML specification does.dir="auto"andbdinow analyze the text of non-HTML elements, such as SVG and MathML, and only ignoreHTML elements such as
script,style, andbdi. The text directly in aniframeis analyzed as well, and thecontents of a
template, which are not part of the document, are not.:dir()no longer lets the contents of atemplateinherit direction from thetemplateor itsancestors, as they are not part of the document. They are left to right unless they specify otherwise.
:dir()now matches abdielement that is the root by its text instead of assuming left to right.:dir()now treats any element without a parent element as left to right, not just the root. Before,all but the first top level element of a fragment parsed with
html.parsernever matched.:dir()now accepts any identifier, as CSS requires, including escaped ones. Values other thanltrandrtldo not match anything.svgelementextracted from an
html5libdocument. Previously every element in such a tree was treated as an HTML element,which affected namespace selectors, namespaced attributes, and HTML specific pseudo-classes.
:lang()treating an element with an emptylangorxml:langattribute as having the language ofa tagged ancestor.
:defaultto avoid quadratic time on forms withmany non-default submit candidates.
:in-rangeand:out-of-rangematching elements whose value,min, ormaxhas a trailing newline;per HTML, such values are not valid, so they are treated like a missing value and cannot be out of range.
:in-rangeand:out-of-rangeraisingValueErrorwhen a date, month, week, or local date andtime
inputhas a year with more digits than Python will convert; such a year is now treated as invalid.:in-rangeand:out-of-rangenot recognizing adatetime-localvalue,min, ormaxthat uses asingle space instead of
Tto separate the date and time.:indeterminateto avoid quadratic timeon forms with many radio button groups.
:lang()now uses thelangattribute of SVG elements, as the HTML specification does (withxml:langtaking precedence). Previously only
xml:langwas considered for elements outside the HTML namespace.:definedno longer matches HTML elements with anisattribute (customized built-in elements), which,like custom elements, are undefined until they are registered.
a ~ band:has(~ b)) toavoid quadratic time when the first matching sibling is far into a long list of siblings.
imodifier using ASCII case insensitivity.inputwith an unknowntype, such astype="foo", as a text input in:placeholder-shown,:read-write,:read-onlyand:dir(), as an invalidtypeis in the Text state in the HTML specification.:enabledand:disablednever matchinginputelements withtype="hidden".:requiredno longer matchesinputelements of types therequiredattribute does not apply to(
hidden,range,color,submit,image,resetandbutton). Like in browsers, they match:optional.a b) toavoid time that grows with the depth of the document to the power of the number of combinators.
v3.0.4Compare Source
3.0.4
:-soup-contains()and:-soup-contains-own().:dir()to avoid quadratic time on deeply nesteddir="auto"elements.autohandling in regard to:dir()selector. When an element withdir="auto"(or a<bdi>)contains no strong direction characters, it should be assumed as left-to-right and not assume the direction of the
parent.
:in-rangeand:out-of-rangeraisingValueErrorforweekinputs whose week-year is 10000 orgreater.
:nth-last-child(an+b of S)matching the wrong elements when the first sibling tested does notmatch
S.escape()now only leaves non-ASCII identifier code points unescaped, as defined by the CSS specification.All other non-ASCII characters are escaped.
:in-rangeand:out-of-rangetreatingtimeanddatetime-localvalues with seconds asinvalid.
v3.0.3Compare Source
3.0.3
>, the `BeautifulSConfiguration
📅 Schedule: (in timezone Europe/Berlin)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, 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.