From c6d34c37651230b660ab68906df8c5f2838cc5d3 Mon Sep 17 00:00:00 2001 From: Ankur Tyagi Date: Mon, 14 Sep 2026 15:12:57 +1200 Subject: [PATCH] python3-sqlparse: patch CVE-2026-71491 Details: https://nvd.nist.gov/vuln/detail/cve-2026-71491 Signed-off-by: Ankur Tyagi Signed-off-by: Anuj Mittal --- .../python3-sqlparse/CVE-2026-71491.patch | 147 ++++++++++++++++++ .../python/python3-sqlparse_0.5.5.bb | 1 + 2 files changed, 148 insertions(+) create mode 100644 meta-python/recipes-devtools/python/python3-sqlparse/CVE-2026-71491.patch diff --git a/meta-python/recipes-devtools/python/python3-sqlparse/CVE-2026-71491.patch b/meta-python/recipes-devtools/python/python3-sqlparse/CVE-2026-71491.patch new file mode 100644 index 0000000000..e0136cc4aa --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-sqlparse/CVE-2026-71491.patch @@ -0,0 +1,147 @@ +From ca01c323483883f205ee7bb44581c674fcfe6e49 Mon Sep 17 00:00:00 2001 +From: Andi Albrecht +Date: Mon, 10 Aug 2026 07:35:42 +0200 +Subject: [PATCH] Fix quadratic DoS in group_comments (GHSA-f2ff-p2ww-7p4p) + +A comment-only statement ('-- c\n' repeated) made group_comments rescan +the whole remaining token tail once per comment token, costing O(n^2). +Because group_comments runs before the MAX_GROUPING_TOKENS guard, the +cost was paid even on oversized input. + +Stop as soon as token_not_matching finds no terminator in the remaining +tokens: from that point on nothing can group, so re-scanning the tail is +wasted work. This makes the pass O(n) while preserving grouping output. + +Add benchmarks/validate_group_comments_dos.py to check the scaling. + +Reported by sanktjodel. + +Co-Authored-By: Claude Opus 4.8 + +(cherry picked from commit ef2012a5eeb491e604dea2b00d516904a3830c87) + +CVE: CVE-2026-71491 +Upstream-Status: Backport [https://github.com/andialbrecht/sqlparse/commit/ef2012a5eeb491e604dea2b00d516904a3830c87] + +Dropped changes to the CHANGELOG file. + +Signed-off-by: Ankur Tyagi +--- + benchmarks/validate_group_comments_dos.py | 85 +++++++++++++++++++++++ + sqlparse/engine/grouping.py | 11 ++- + 2 files changed, 93 insertions(+), 3 deletions(-) + create mode 100644 benchmarks/validate_group_comments_dos.py + +diff --git a/benchmarks/validate_group_comments_dos.py b/benchmarks/validate_group_comments_dos.py +new file mode 100644 +index 0000000..58b3e01 +--- /dev/null ++++ b/benchmarks/validate_group_comments_dos.py +@@ -0,0 +1,85 @@ ++"""Validate that ``group_comments`` scales linearly on comment-only input. ++ ++Regression check for the quadratic O(n^2) DoS in ``group_comments`` ++(sqlparse/engine/grouping.py), reported as GHSA-f2ff-p2ww-7p4p. ++ ++A statement made only of single-line comments (``'-- c\\n'`` repeated n times) ++lexes in O(n) but ``group_comments`` rescans the O(n) remaining tokens for every ++comment token, giving O(n^2) total work. ``group_comments`` runs first in ++``group()``, before the ``MAX_GROUPING_TOKENS`` guard, so the token cap does not ++protect this vector. The path is reachable via ``sqlparse.parse()`` and ++``sqlparse.format(sql, strip_comments=True)``. ++ ++This script measures the scaling of the vulnerable path and reports whether the ++observed growth is quadratic (vulnerable) or roughly linear (patched). ++ ++Run with: python benchmarks/validate_group_comments_dos.py ++ ++Exit code 0 => behaviour looks linear (advisory mitigated). ++Exit code 1 => behaviour looks quadratic (advisory reproduced). ++""" ++ ++import sys ++import time ++ ++import sqlparse ++ ++ ++def payload(n): ++ """A comment-only statement of n single-line comments.""" ++ return '-- c\n' * n ++ ++ ++def measure(fn, sql): ++ t0 = time.perf_counter() ++ fn(sql) ++ return (time.perf_counter() - t0) * 1000 ++ ++ ++def run(label, fn): ++ print(f'{label}:') ++ sizes = (1000, 2000, 4000, 8000) ++ timings = [] ++ for n in sizes: ++ dt = measure(fn, payload(n)) ++ timings.append(dt) ++ print(f' n={n:5d} {dt:8.1f} ms ({len(payload(n))} B)') ++ ++ # For each doubling of the input, quadratic growth ~4x, linear ~2x. ++ ratios = [b / a for a, b in zip(timings, timings[1:]) if a > 0] ++ print(f' doubling ratios: {", ".join(f"{r:.2f}x" for r in ratios)}') ++ return ratios ++ ++ ++def classify(ratios): ++ """Quadratic if the average per-doubling ratio is closer to 4x than 2x.""" ++ if not ratios: ++ return 'inconclusive', 0.0 ++ avg = sum(ratios) / len(ratios) ++ # Midpoint between linear (2x) and quadratic (4x) is 3x. ++ return ('quadratic' if avg >= 3.0 else 'linear'), avg ++ ++ ++def main(): ++ print('GHSA-f2ff-p2ww-7p4p: quadratic DoS in group_comments\n') ++ ++ all_ratios = [] ++ all_ratios += run('sqlparse.parse', sqlparse.parse) ++ print() ++ all_ratios += run( ++ 'sqlparse.format(strip_comments=True)', ++ lambda s: sqlparse.format(s, strip_comments=True), ++ ) ++ print() ++ ++ verdict, avg = classify(all_ratios) ++ print(f'Average doubling ratio: {avg:.2f}x => {verdict}') ++ if verdict == 'quadratic': ++ print('VULNERABLE: growth is quadratic, advisory reproduced.') ++ return 1 ++ print('OK: growth is roughly linear, advisory mitigated.') ++ return 0 ++ ++ ++if __name__ == '__main__': ++ sys.exit(main()) +diff --git a/sqlparse/engine/grouping.py b/sqlparse/engine/grouping.py +index 43ca5b5..7a3ca1a 100644 +--- a/sqlparse/engine/grouping.py ++++ b/sqlparse/engine/grouping.py +@@ -339,9 +339,14 @@ def group_comments(tlist): + while token: + eidx, end = tlist.token_not_matching( + lambda tk: imt(tk, t=T.Comment) or tk.is_newline, idx=tidx) +- if end is not None: +- eidx, end = tlist.token_prev(eidx, skip_ws=False) +- tlist.group_tokens(sql.Comment, tidx, eidx) ++ if end is None: ++ # From tidx onward everything is comment/newline: there is no ++ # terminator to group against, and every later start would hit ++ # the same dead end. Stop instead of re-scanning the tail once ++ # per remaining comment token (which is O(n**2)). ++ break ++ eidx, end = tlist.token_prev(eidx, skip_ws=False) ++ tlist.group_tokens(sql.Comment, tidx, eidx) + + tidx, token = tlist.token_next_by(t=T.Comment, idx=tidx) + diff --git a/meta-python/recipes-devtools/python/python3-sqlparse_0.5.5.bb b/meta-python/recipes-devtools/python/python3-sqlparse_0.5.5.bb index a23f5ec0e2..61fe759ede 100644 --- a/meta-python/recipes-devtools/python/python3-sqlparse_0.5.5.bb +++ b/meta-python/recipes-devtools/python/python3-sqlparse_0.5.5.bb @@ -10,6 +10,7 @@ SRC_URI += "file://CVE-2026-54284-1.patch \ file://CVE-2026-54284-2.patch \ file://CVE-2026-59893.patch \ file://CVE-2026-59894.patch \ + file://CVE-2026-71491.patch \ " CVE_PRODUCT = "sqlparse"