Download Latest Version 0.65.14 -- SELECT-only really means SELECT-only in SQLChatAgent source code.zip (59.3 MB)
Email in envelope

Get an email when there's a new version of Langroid

Home / 0.65.14
Name Modified Size InfoDownloads / 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:

  • MERGE actions are exempt. sqlglot parses WHEN MATCHED THEN UPDATE as a child Update node, so an operator allowing MERGE would otherwise have been forced to allow standalone UPDATE/INSERT as well.
  • …but only the action itself. The exemption matches the WHEN ... THEN node by identity. Matching anything beneath a WHEN would have hidden a write in the WHEN condition — turning the exemption into a smuggling route.
  • MySQL SELECT ... INTO @var is not a table creation. It assigns a user variable and writes nothing, but sqlglot models the target as Table(this=Parameter(...)); treating it as a write rejected a legitimate read. SELECT ... INTO t UNION ALL SELECT ... is now caught too, where the into sits on a branch and the top node is a Union.

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

Source: README.md, updated 2026-07-26