CVE-2026-54760: langroid SQLChatAgent pg_read_file Blocklist Bypass
Langroid's SQLChatAgent blocks dangerous PostgreSQL file-read functions by regex, but quoting the function name, inserting an inline comment, or schema-qualifying it bypasses the check entirely, letti
The problem
SQLChatAgent's `_validate_query` runs LLM-generated SQL through a raw-text regex blocklist (`_DANGEROUS_SQL_PATTERNS`) and then a `sqlglot` SELECT-only allowlist. The blocklist entry for `pg_read_file` and related functions requires the token to be immediately followed by optional whitespace and `(`, written as `\bpg_(read|stat|ls|current_logfile)[A-Za-z0-9_]*\s*\(`.
PostgreSQL accepts the same call when the function name is double-quoted, separated from `(` by an inline `/**/` comment, or prefixed with `pg_catalog.` schema qualification. All three forms parse as valid `SELECT` statements, so they pass the sqlglot allowlist check too.
The result is that `pg_read_file` calls that the regex was meant to block reach the database and return file contents.
Proof of concept
A working proof-of-concept for CVE-2026-54760 in langroid, with the exact payload below.
-- three confirmed bypass forms, all pass _validate_query and execute on PostgreSQL:
-- 1. quoted identifier
SELECT "pg_read_file"('/etc/passwd');
-- 2. inline comment between name and paren
SELECT pg_read_file/**/('/etc/passwd');
-- 3. schema-qualified quoted name
SELECT pg_catalog."pg_read_file"('/etc/passwd');The regex anchors on the literal token `pg_read_file` followed by `\s*\(`. PostgreSQL's parser normalises quoted identifiers and strips comments before function resolution, so the database sees the same call in all three forms while the Python regex sees none of them as matching.
Because `_validate_query` only uses sqlglot to enforce the top-level statement type (SELECT) and never inspects the parsed AST for function names, the normalised call is never checked after parsing. The root cause is CWE-184 (Incomplete List of Disallowed Inputs) combined with relying on raw-text pattern matching instead of AST-level analysis for a security gate.
The fix replaces the regex check with a sqlglot AST walk that strips quoting and schema prefixes before comparing the function name against a denylist.
The fix
Upgrade to langroid 0.65.1. The patch replaces the vulnerable raw-text regex approach with a sqlglot AST traversal that normalises (unquotes, lowercases, strips schema prefixes) every function invocation in the parsed tree and rejects any whose bare name appears in the dangerous-function denylist.
As a defence-in-depth measure, also connect SQLChatAgent to a PostgreSQL role that does not hold the `pg_read_server_files` privilege or superuser status, so `pg_read_file` calls are rejected at the database level even if the application-layer check is bypassed.
Related research
- criticalCVE-2026-55615CVE-2026-55615: langroid Neo4jChatAgent Prompt-to-Cypher Injection (RCE)
- high · 8.1CVE-2026-54771CVE-2026-54771: langroid Unauthorized Tool Invocation via User-Supplied JSON
- high · 7.8CVE-2026-53925CVE-2026-53925: Glances Arbitrary File Write via secure_popen Operator Injection in AMP Configuration
- highCVE-2026-55488CVE-2026-55488: motionEye Absolute Path Traversal in Media File Handlers