python3-sqlparse: patch CVE-2026-54284

Detais:
https://nvd.nist.gov/vuln/detail/cve-2026-54284

Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
Ankur Tyagi authored and Anuj Mittal committed 2026-10-05 14:40:45 +05:30
1 parent 0bfdd495a4
commit bf592926cd
3 files changed
+185

No files matched your search

@@ -0,0 +1,37 @@
From 1a0f6530c8f506f953d1b4ffdfa84ae7ad72f7df Mon Sep 17 00:00:00 2001
From: alhudz <al.hudz.k@gmail.com>
Date: Mon, 1 Jun 2026 19:05:57 +0530
Subject: [PATCH] set group value from child tokens to avoid quadratic grouping
(cherry picked from commit 939b129e24c0ad5d51368b1aa72fffcaca76f06f)
CVE: CVE-2026-54284
Upstream-Status: Backport [https://github.com/andialbrecht/sqlparse/commit/939b129e24c0ad5d51368b1aa72fffcaca76f06f]
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
sqlparse/sql.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sqlparse/sql.py b/sqlparse/sql.py
index 831dfb9..0163ff1 100644
--- a/sqlparse/sql.py
+++ b/sqlparse/sql.py
@@ -159,7 +159,7 @@ class TokenList(Token):
def __init__(self, tokens=None):
self.tokens = tokens or []
[setattr(token, 'parent', self) for token in self.tokens]
- super().__init__(None, str(self))
+ super().__init__(None, ''.join(token.value for token in self.tokens))
self.is_group = True
def __str__(self):
@@ -322,7 +322,7 @@ class TokenList(Token):
grp = start
grp.tokens.extend(subtokens)
del self.tokens[start_idx + 1:end_idx]
- grp.value = str(start)
+ grp.value += ''.join(token.value for token in subtokens)
else:
subtokens = self.tokens[start_idx:end_idx]
grp = grp_cls(subtokens)
@@ -0,0 +1,144 @@
From 6416e171b41da8939f391c0492b355d3b6cb8c11 Mon Sep 17 00:00:00 2001
From: Andi Albrecht <albrecht.andi@gmail.com>
Date: Sat, 6 Jun 2026 07:12:43 +0200
Subject: [PATCH] Add tests from PR, update CHANGELOG and AUTHORS.
(cherry picked from commit f80af6a4007f11ada847218df8c29dc859238290)
CVE: CVE-2026-54284
Upstream-Status: Backport [https://github.com/andialbrecht/sqlparse/commit/f80af6a4007f11ada847218df8c29dc859238290]
Dropped changes to the CHANGELOG file.
Signed-off-by: Ankur Tyagi <ankur.tyagi85@gmail.com>
---
AUTHORS | 2 ++
benchmarks/bench_grouping.py | 59 ++++++++++++++++++++++++++++++++++++
tests/test_dos_prevention.py | 28 +++++++++++++++++
3 files changed, 89 insertions(+)
create mode 100644 benchmarks/bench_grouping.py
diff --git a/AUTHORS b/AUTHORS
index 24ca667..872c700 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -12,6 +12,7 @@ Alphabetical list of contributors:
* Aki Ariga <chezou+github@gmail.com>
* Alexander Beedie <ayembee@gmail.com>
* Alexey Malyshev <nostrict@gmail.com>
+* alhudz <al.hudz.k@gmail.com>
* ali-tny <aliteeney@googlemail.com>
* andrew deryabin <github@djsf.com>
* Andrew Tipton <andrew.tipton@compareglobalgroup.com>
@@ -77,6 +78,7 @@ Alphabetical list of contributors:
* Tao Wang <twang2218@gmail.com>
* Tenghuan <tenghuanhe@gmail.com>
* Tim Graham <timograham@gmail.com>
+* tonghuaroot <tonghuaroot@users.noreply.github.com>
* Victor Hahn <info@victor-hahn.de>
* Victor Uriarte <vmuriart@gmail.com>
* Ville Skyttä <ville.skytta@iki.fi>
diff --git a/benchmarks/bench_grouping.py b/benchmarks/bench_grouping.py
new file mode 100644
index 0000000..245ea0e
--- /dev/null
+++ b/benchmarks/bench_grouping.py
@@ -0,0 +1,59 @@
+"""Grouping performance benchmarks.
+
+Measures parse time for SQL patterns that stress the grouping engine:
+- Deeply nested parentheses
+- Deeply nested CASE WHEN expressions
+- Wide column lists (tests O(N) identifier grouping, fixed in PR848)
+
+Run with: python benchmarks/bench_grouping.py
+"""
+
+import signal
+import time
+
+import sqlparse
+
+
+def _alarm_handler(signum, frame):
+ raise TimeoutError()
+
+
+signal.signal(signal.SIGALRM, _alarm_handler)
+
+
+def measure(label, sql, fn):
+ signal.alarm(30)
+ t0 = time.perf_counter()
+ status = 'OK'
+ try:
+ fn(sql)
+ except sqlparse.exceptions.SQLParseError:
+ status = 'CAP'
+ except TimeoutError:
+ status = 'TIMEOUT'
+ finally:
+ signal.alarm(0)
+ dt = (time.perf_counter() - t0) * 1000
+ print(f' {status:8} {dt:8.1f} ms {label} ({len(sql)} B)')
+
+
+# Vector 1: deeply nested parentheses
+print('Nested parentheses:')
+for n in (200, 500, 1000, 2000):
+ sql = 'SELECT ' + '(' * n + '1' + ')' * n
+ measure(f'nested-paren n={n}', sql, sqlparse.parse)
+
+# Vector 2: deeply nested CASE WHEN
+print('Nested CASE WHEN:')
+for n in (100, 200, 400):
+ case = '1'
+ for i in range(n):
+ case = f'CASE WHEN x={i} THEN {case} ELSE NULL END'
+ measure(f'CASE-nested n={n}', f'SELECT {case} FROM t', sqlparse.parse)
+
+# Vector 3: wide column lists (O(N) grouping, regression fixed in PR848)
+print('Wide column lists:')
+for n in (500, 1000, 2000, 4000):
+ cols = ', '.join(f'col_{i}' for i in range(n))
+ sql = f'SELECT {cols} FROM t'
+ measure(f'wide-select n={n}', sql, sqlparse.parse)
diff --git a/tests/test_dos_prevention.py b/tests/test_dos_prevention.py
index 4e826c5..1753c05 100644
--- a/tests/test_dos_prevention.py
+++ b/tests/test_dos_prevention.py
@@ -50,6 +50,34 @@ class TestDoSPrevention:
with pytest.raises(SQLParseError, match="Maximum number of tokens exceeded"):
sqlparse.format(sql, reindent=True)
+ def test_nested_paren_within_cap_under_1s(self):
+ """Reaching MAX_GROUPING_DEPTH must not require multi-second CPU.
+
+ Before the TokenList.__init__ fix, a 1 KB payload of 500 nested
+ parens took ~1.3 s and a 2 KB payload of 1000 nested parens took
+ ~11 s before the depth cap raised SQLParseError, because each
+ TokenList materialised its ``value`` via ``str(self)`` which
+ recursed over the full subtree (O(n * depth)).
+ """
+ sql = 'SELECT ' + '(' * 1000 + '1' + ')' * 1000
+ t0 = time.perf_counter()
+ with pytest.raises(SQLParseError, match='Maximum grouping depth'):
+ sqlparse.parse(sql)
+ dt = time.perf_counter() - t0
+ assert dt < 1.0, f'parse took {dt:.2f}s, expected sub-second'
+
+ def test_nested_case_within_cap_under_1s(self):
+ """Same invariant as nested parentheses, exercised via CASE WHEN."""
+ case = '1'
+ for i in range(400):
+ case = f'CASE WHEN x={i} THEN {case} ELSE NULL END'
+ sql = f'SELECT {case} FROM t'
+ t0 = time.perf_counter()
+ with pytest.raises(SQLParseError, match='Maximum grouping depth'):
+ sqlparse.parse(sql)
+ dt = time.perf_counter() - t0
+ assert dt < 1.0, f'parse took {dt:.2f}s, expected sub-second'
+
def test_normal_sql_still_works(self):
"""Test that normal SQL still works correctly after DoS protections."""
sql = """
@@ -6,6 +6,10 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=2b136f573f5386001ea3b7b9016222fc"
SRC_URI[sha256sum] = "e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e"
SRC_URI += "file://CVE-2026-54284-1.patch \
file://CVE-2026-54284-2.patch \
"
CVE_PRODUCT = "sqlparse"
export BUILD_SYS