{"id":"PYSEC-2026-3923","summary":"sqlparse: Reindentation of tuple lists causes near-cap quadratic CPU consumption","details":"### Summary\n\nWhen SQL is formatted with reindentation enabled, `ReindentFilter` repeatedly rebuilds prefixes of the current statement to calculate token offsets. An attacker who controls SQL sent to this opt-in formatting path can supply a parenthesized tuple list that remains just below the grouping-token cap. Thousands of offset calculations then traverse an expanding token tree, causing multi-second CPU consumption from an input of roughly 16 KB and degrading service availability.\n\n### Details\n\n`ReindentFilter._get_offset()` joins the tokens returned by `_flatten_up_to_token()` to calculate the current output position. Each call begins by flattening the current statement from its start and walks until the target token. Tuple-list reindentation invokes this calculation repeatedly as it processes many parenthesized values, so later calls redo an increasingly large amount of prior work.\n\nThe vulnerable path is reached through `sqlparse.format(sql, reindent=True)` and `sqlformat --reindent`. A carefully sized tuple list completes grouping below the configured token cap and then enters the expensive reindentation path; a slightly larger input may instead be rejected quickly by the cap.\n\nRelevant code locations:\n\n- `sqlparse/formatter.py:170` — enabling `ReindentFilter`\n- `sqlparse/filters/reindent.py:30` — repeated flattening from the statement start\n- `sqlparse/filters/reindent.py:44` — prefix joining for offset calculation\n- `sqlparse/filters/reindent.py:216` — tuple-list processing path\n\n### PoC\n\nA complete validated reproduction is attached as [reindent_tuple_list_cpu_dos-poc.zip](https://github.com/user-attachments/files/29410152/reindent_tuple_list_cpu_dos-poc.zip). The archive contains `reproduction/` at its root, uses Git and Docker, and compares two same-shape tuple-list inputs formatted with reindentation enabled.\n\nExtract the archive beside this report, then run:\n\n```console\n./reproduction/run.sh\n```\n\nObserved result:\n\nThe 600-tuple baseline completed in 0.649 seconds, while the below-cap 1,425-tuple input completed in 4.999 seconds. The run emitted `EVOHUNT_REINDENT_DOS_VERIFIED` and completed successfully.\n\nVerification method:\n\nThe verification helper formats two same-shape tuple-list payloads with `reindent=True` and fails unless the larger payload completes successfully, takes at least 2.0 seconds, and takes at least 4x the baseline.\n\nLimitations:\n\nNo reproduction blocker was recorded. Timing varies by host, and exploitation requires the reindentation option or corresponding CLI mode to be enabled.\n\n### Impact\n\nThis is a CPU resource-exhaustion vulnerability in workflows that reindent attacker-controlled SQL. A small crafted tuple-list input can occupy a worker for several seconds, enabling request delays, reduced throughput, or worker starvation when payloads are processed repeatedly or concurrently.\n\nThe affected reindentation behavior is opt-in, and sqlparse itself does not provide network exposure; reachability depends on the consuming application or CLI workflow. The demonstrated effect is CPU consumption in a single formatting call, not process termination, code execution, or confidentiality or integrity impact.","aliases":["CVE-2026-84305","GHSA-cfqr-cjx5-5jcm"],"modified":"2026-09-10T12:15:14.814559660Z","published":"2026-09-10T09:44:59.097680Z","references":[{"type":"WEB","url":"https://github.com/andialbrecht/sqlparse/security/advisories/GHSA-cfqr-cjx5-5jcm"},{"type":"WEB","url":"https://github.com/andialbrecht/sqlparse/commit/a51df6d9e2d31b44be9adb6bc8732517db6bf96b"},{"type":"PACKAGE","url":"https://github.com/andialbrecht/sqlparse"},{"type":"WEB","url":"https://github.com/andialbrecht/sqlparse/releases/tag/0.6.0"},{"type":"PACKAGE","url":"https://pypi.org/project/sqlparse"},{"type":"ADVISORY","url":"https://github.com/advisories/GHSA-cfqr-cjx5-5jcm"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-84305"}],"affected":[{"package":{"name":"sqlparse","ecosystem":"PyPI","purl":"pkg:pypi/sqlparse"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.6.0"}]}],"versions":["0.1.0","0.1.1","0.1.10","0.1.11","0.1.12","0.1.13","0.1.14","0.1.15","0.1.16","0.1.17","0.1.18","0.1.19","0.1.2","0.1.3","0.1.4","0.1.5","0.1.6","0.1.7","0.1.8","0.1.9","0.2.0","0.2.1","0.2.2","0.2.3","0.2.4","0.3.0","0.3.1","0.4.0","0.4.1","0.4.2","0.4.3","0.4.4","0.5.0","0.5.1","0.5.2","0.5.3","0.5.4","0.5.5"],"database_specific":{"source":"https://github.com/pypa/advisory-database/blob/main/vulns/sqlparse/PYSEC-2026-3923.yaml"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V4","score":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N"}]}