| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| Parent folder | |||
| langroid-0.65.14-py3-none-any.whl | < 15 hours ago | 458.3 kB | |
| langroid-0.65.14.tar.gz | < 15 hours ago | 407.7 kB | |
| 0.65.14 -- SELECT-only really means SELECT-only in SQLChatAgent source code.tar.gz | < 15 hours ago | 58.8 MB | |
| 0.65.14 -- SELECT-only really means SELECT-only in SQLChatAgent source code.zip | < 15 hours ago | 59.3 MB | |
| README.md | < 15 hours ago | 3.9 kB | |
| Totals: 5 Items | 118.9 MB | 0 | |
0.65.14 — SELECT-only really means SELECT-only in SQLChatAgent
SQLChatAgent's statement-type allowlist classified a query by its top-level
parse node alone. Two shapes perform writes without a write node at the top, so
both were accepted as SELECT under the default policy and executed:
- a data-modifying CTE —
WITH x AS (DELETE FROM t RETURNING *) SELECT * FROM x -
SELECT ... INTO tbl, which creates and populates a table:::bash pip install -U langroid
What was wrong
The allowlist is the control that makes the default configuration safe even
when the agent's database role can write. That guarantee did not hold: both
shapes parse with a Select top node, so kind resolved to "SELECT",
validation passed, and the statement was executed and committed while the
engine performed the write.
Unlike the dangerous-function denylists, this needed no superuser and no special extension — an ordinary write-capable role was enough. Verified against sqlglot:
writable CTE top=Select nested_writes=['Delete']
SELECT INTO top=Select into=True
The fix
A new _nested_write_kinds() walks the parsed statement for
Insert/Update/Delete/Merge/Create/Drop/Alter/TruncateTable
nodes below the top level, and separately flags the SELECT ... INTO form.
Any nested kind outside allowed_statement_types is rejected, naming what it
found.
This is a bounded fix rather than another denylist entry: statement kinds are a closed set, so walking the tree closes the whole class.
Three refinements came out of adversarial review, each with its own regression test:
MERGEactions are exempt. sqlglot parsesWHEN MATCHED THEN UPDATEas a childUpdatenode, so an operator allowingMERGEwould otherwise have been forced to allow standaloneUPDATE/INSERTas well.- …but only the action itself. The exemption matches the
WHEN ... THENnode by identity. Matching anything beneath aWHENwould have hidden a write in theWHENcondition — turning the exemption into a smuggling route. - MySQL
SELECT ... INTO @varis not a table creation. It assigns a user variable and writes nothing, but sqlglot models the target asTable(this=Parameter(...)); treating it as a write rejected a legitimate read.SELECT ... INTO t UNION ALL SELECT ...is now caught too, where theintosits on a branch and the top node is aUnion.
Behavior otherwise unchanged: read-only CTEs still pass, an operator who
extends allowed_statement_types still gets exactly the writes they
authorized, and allow_dangerous_operations=True still bypasses every check.
Upgrade notes
If you run SQLChatAgent against a write-capable role, upgrade. If your
database role is read-only — the configuration
SECURITY.md (github.com)
recommends — you were never exposed to this. No API changes.
Scope policy
SECURITY.md was corrected alongside the fix. Allowlist misclassification had
been wrongly grouped with denylist bypasses and declared out of scope; it is a
bounded, fixable defect and is now explicitly in scope. The denylists
themselves remain best-effort hardening, not a security boundary — the real
boundary is the privileges of the database credential you hand the agent, plus
the container and filesystem the process runs in.
Credit
Reported by @manus-use
(GHSA-3gpx-vwr3-xvwx), and earlier by
@LHMisme420 (GHSA-wc83-4cvx-p8xc) — the
earlier report was closed in error and the second one prompted the correction.
The MERGE regression was caught by the GitHub Codex reviewer on
#1074.
Upgrade
:::bash
pip install -U langroid
Full changelog: 0.65.13...0.65.14