Langroid: SQLChatAgent _validate_query blocklist misses pg_read_file family enabling arbitrary file read (CVE-2026-50180)
### Summary `SQLChatAgent` in `langroid` ships a `_validate_query` defense-in-depth layer whose `_DANGEROUS_SQL_PATTERNS` regex blocklist enumerates dangerous SQL primitives by specific function name. The list misses the canonical PostgreSQL filesystem-disclosure family `pg_read_file()`, `pg_stat_file()`, `pg_ls_logdir()`, `pg_ls_waldir()`, `pg_current_logfile()` (and similar `SELECT`-shaped functions in the same family). It also leaves SQL Server `OPENDATASOURCE` and SQLite `ATTACH '<file>' AS x` (DATABASE keyword omitted) unblocked. An attacker able to shape the LLM's generated SQL (directly via prompt input or transitively via prompt-injection in data the LLM ingests) can read arbitrary files from the PostgreSQL host through ordinary `SELECT` queries, even with the agent's strict default configuration (`allow_dangerous_operations=False`, `allowed_statement_types=['SELECT']`). The payloads survive the statement-type allowlist (each is a `SELECT`) and pass through the regex blocklist (none of the function names match), then reach the live SQLAlchemy engine via `SQLChatAgent.run_query`. ### Affected versions `langroid` `<= 0.63.0` (latest at the time of this report; PyPI release 2026-05-27). The vulnerable code path is `langroid/agent/special/sql/sql_chat_agent.py::_validate_query`, which consults the module-level `_DANGEROUS_SQL_PATTERNS` literal at `sql_chat_agent.py:113-141`. ### Privilege required Any caller able to influence the LLM-generated `RunQueryTool.query` string that reaches `SQLChatAgent.run_query`. In a typical deployment this is any client of a SQLChatAgent-backed service, or any upstream data source whose content the LLM is asked to read and summarise. No PostgreSQL credentials are required from the attacker; the agent holds them. ### Vulnerable code `langroid/agent/special/sql/sql_chat_agent.py:113-141` (the `_DANGEROUS_SQL_PATTERNS` literal) and `sql_chat_agent.py:546-615` (the `_validate_query` method that consults it): ```python # sql_chat_agent.py:113 _DANGEROUS_SQL_PATTERNS: List["re.Pattern[str]"] = [ re.compile(r"\bcopy\b[\s\S]*\bprogram\b", re.IGNORECASE), re.compile(r"\bpg_read_server_files?\b", re.IGNORECASE), re.compile(r"\bpg_read_binary_file\b", re.IGNORECASE), re.compile(r"\bpg_ls_dir\b", re.IGNORECASE), re.compile(r"\blo_(import|export)\b", re.IGNORECASE), re.compile(r"\binto\s+(outfile|dumpfile)\b", re.IGNORECASE), re.compile(r"\bload_file\s*\(", re.IGNORECASE), re.compile(r"\bload\s+data\b", re.IGNORECASE), re.compile(r"\bload_extension\s*\(", re.IGNORECASE), re.compile(r"\battach\s+database\b", re.IGNORECASE), re.compile(r"\bxp_cmdshell\b", re.IGNORECASE), re.compile(r"\bsp_oacreate\b", re.IGNORECASE), re.compile(r"\bsp_oamethod\b", re.IGNORECASE), re.compile(r"\bopenrowset\b", re.IGNORECASE), re.compile(r"\bbulk\s+insert\b", re.IGNORECASE), re.compile( r"\bcreate\s+(or\s+replace\s+)?(function|procedure|trigger)\b", re.IGNORECASE, ), re.compile(r"\bcreate\s+extension\b", re.IGNORECASE), ] ``` The blocklist is a list of `\b<exact-token>\b` literals. PostgreSQL ships several near-name functions on the same primitive that none of these match: | Function | What it returns | Matched by blocklist? | |---|---|---| | `pg_read_server_file('/path')` | file contents | yes (`pg_read_server_files?`) | | `pg_read_binary_file('/path')` | binary contents | yes | | `pg_ls_dir('/path')` | directory listing | yes | | `pg_read_file('/path')` | file contents | **no** (no `_server_` infix) | | `pg_stat_file('/path')` | size, mtime, ctime, atime, isdir | **no** | | `pg_ls_logdir()` | filenames in PostgreSQL log dir | **no** | | `pg_ls_waldir()` | WAL filenames and sizes | **no** | | `pg_ls_tmpdir()` | temp-dir listing | **no** | | `pg_ls_archive_statusdir()` | archive-status directory listing | **no** | | `pg_current_logfile()` | active server log path | **no** | Each of these is a `SELECT`-shaped function call. They pass the `sqlglot_exp.Select`-only statement-type allowlist applied at `sql_chat_agent.py:583-614`, then evade the regex blocklist (their names contain no token the blocklist enumerates), then reach the SQLAlchemy `session.execute(text(query))` sink inside `SQLChatAgent.run_query` (line 631 onwards). Two non-PostgreSQL secondary gaps with the same regex-enumeration shape: - The SQLite pattern `\battach\s+database\b` requires the literal `DATABASE` keyword. Per the SQLite grammar (https://www.sqlite.org/lang_attach.html) the keyword is optional: `ATTACH '/path/to/db' AS x` is valid syntax and matches no entry in the blocklist. Whether the agent rejects this via the statement-type allowlist depends on how the configured `sqlglot` dialect parses it; on PostgreSQL dialect parsing fails (sqlglot returns no `Select`) and the statement-type check rejects, but a SQLite-dialect SQLChatAgent (`database_uri="sqlite:///..."`) returns the statement as `sqlglot_exp.Attach`, whi
AI Analysis
Technical Summary
The SQLChatAgent in langroid uses a regex-based blocklist (_DANGEROUS_SQL_PATTERNS) to prevent dangerous SQL operations by blocking specific function names. However, this blocklist omits several PostgreSQL filesystem-disclosure functions such as pg_read_file(), pg_stat_file(), pg_ls_logdir(), pg_ls_waldir(), and pg_current_logfile(), which return file contents or directory listings. These functions are callable via SELECT statements, which pass the agent's statement-type allowlist and evade the regex blocklist, reaching the SQLAlchemy execution engine. Consequently, an attacker able to influence the LLM's generated SQL query string can read arbitrary files on the PostgreSQL host without needing database credentials. Similar gaps exist for SQLite's ATTACH without the DATABASE keyword and SQL Server's OPENDATASOURCE. The affected versions are langroid <= 0.63.0. No patch or official fix is currently available.
Potential Impact
An attacker who can influence the SQL queries generated by the LLM in langroid's SQLChatAgent can read arbitrary files from the PostgreSQL host filesystem. This can lead to unauthorized disclosure of sensitive data stored on the database server. The attack requires no direct database credentials from the attacker, as the agent holds them. The vulnerability bypasses existing query type restrictions and regex-based blocklists, making it a high-severity information disclosure risk.
Mitigation Recommendations
Patch status is not yet confirmed — check the vendor advisory for current remediation guidance. Until an official fix is released, restrict or sanitize inputs that influence the LLM-generated SQL queries to prevent injection of dangerous function calls. Consider disabling or limiting the use of SQLChatAgent features that allow arbitrary query execution, or deploy additional query validation layers that explicitly block the missing PostgreSQL functions (e.g., pg_read_file, pg_stat_file, pg_ls_logdir, pg_ls_waldir, pg_current_logfile).
Langroid: SQLChatAgent _validate_query blocklist misses pg_read_file family enabling arbitrary file read (CVE-2026-50180)
Description
### Summary `SQLChatAgent` in `langroid` ships a `_validate_query` defense-in-depth layer whose `_DANGEROUS_SQL_PATTERNS` regex blocklist enumerates dangerous SQL primitives by specific function name. The list misses the canonical PostgreSQL filesystem-disclosure family `pg_read_file()`, `pg_stat_file()`, `pg_ls_logdir()`, `pg_ls_waldir()`, `pg_current_logfile()` (and similar `SELECT`-shaped functions in the same family). It also leaves SQL Server `OPENDATASOURCE` and SQLite `ATTACH '<file>' AS x` (DATABASE keyword omitted) unblocked. An attacker able to shape the LLM's generated SQL (directly via prompt input or transitively via prompt-injection in data the LLM ingests) can read arbitrary files from the PostgreSQL host through ordinary `SELECT` queries, even with the agent's strict default configuration (`allow_dangerous_operations=False`, `allowed_statement_types=['SELECT']`). The payloads survive the statement-type allowlist (each is a `SELECT`) and pass through the regex blocklist (none of the function names match), then reach the live SQLAlchemy engine via `SQLChatAgent.run_query`. ### Affected versions `langroid` `<= 0.63.0` (latest at the time of this report; PyPI release 2026-05-27). The vulnerable code path is `langroid/agent/special/sql/sql_chat_agent.py::_validate_query`, which consults the module-level `_DANGEROUS_SQL_PATTERNS` literal at `sql_chat_agent.py:113-141`. ### Privilege required Any caller able to influence the LLM-generated `RunQueryTool.query` string that reaches `SQLChatAgent.run_query`. In a typical deployment this is any client of a SQLChatAgent-backed service, or any upstream data source whose content the LLM is asked to read and summarise. No PostgreSQL credentials are required from the attacker; the agent holds them. ### Vulnerable code `langroid/agent/special/sql/sql_chat_agent.py:113-141` (the `_DANGEROUS_SQL_PATTERNS` literal) and `sql_chat_agent.py:546-615` (the `_validate_query` method that consults it): ```python # sql_chat_agent.py:113 _DANGEROUS_SQL_PATTERNS: List["re.Pattern[str]"] = [ re.compile(r"\bcopy\b[\s\S]*\bprogram\b", re.IGNORECASE), re.compile(r"\bpg_read_server_files?\b", re.IGNORECASE), re.compile(r"\bpg_read_binary_file\b", re.IGNORECASE), re.compile(r"\bpg_ls_dir\b", re.IGNORECASE), re.compile(r"\blo_(import|export)\b", re.IGNORECASE), re.compile(r"\binto\s+(outfile|dumpfile)\b", re.IGNORECASE), re.compile(r"\bload_file\s*\(", re.IGNORECASE), re.compile(r"\bload\s+data\b", re.IGNORECASE), re.compile(r"\bload_extension\s*\(", re.IGNORECASE), re.compile(r"\battach\s+database\b", re.IGNORECASE), re.compile(r"\bxp_cmdshell\b", re.IGNORECASE), re.compile(r"\bsp_oacreate\b", re.IGNORECASE), re.compile(r"\bsp_oamethod\b", re.IGNORECASE), re.compile(r"\bopenrowset\b", re.IGNORECASE), re.compile(r"\bbulk\s+insert\b", re.IGNORECASE), re.compile( r"\bcreate\s+(or\s+replace\s+)?(function|procedure|trigger)\b", re.IGNORECASE, ), re.compile(r"\bcreate\s+extension\b", re.IGNORECASE), ] ``` The blocklist is a list of `\b<exact-token>\b` literals. PostgreSQL ships several near-name functions on the same primitive that none of these match: | Function | What it returns | Matched by blocklist? | |---|---|---| | `pg_read_server_file('/path')` | file contents | yes (`pg_read_server_files?`) | | `pg_read_binary_file('/path')` | binary contents | yes | | `pg_ls_dir('/path')` | directory listing | yes | | `pg_read_file('/path')` | file contents | **no** (no `_server_` infix) | | `pg_stat_file('/path')` | size, mtime, ctime, atime, isdir | **no** | | `pg_ls_logdir()` | filenames in PostgreSQL log dir | **no** | | `pg_ls_waldir()` | WAL filenames and sizes | **no** | | `pg_ls_tmpdir()` | temp-dir listing | **no** | | `pg_ls_archive_statusdir()` | archive-status directory listing | **no** | | `pg_current_logfile()` | active server log path | **no** | Each of these is a `SELECT`-shaped function call. They pass the `sqlglot_exp.Select`-only statement-type allowlist applied at `sql_chat_agent.py:583-614`, then evade the regex blocklist (their names contain no token the blocklist enumerates), then reach the SQLAlchemy `session.execute(text(query))` sink inside `SQLChatAgent.run_query` (line 631 onwards). Two non-PostgreSQL secondary gaps with the same regex-enumeration shape: - The SQLite pattern `\battach\s+database\b` requires the literal `DATABASE` keyword. Per the SQLite grammar (https://www.sqlite.org/lang_attach.html) the keyword is optional: `ATTACH '/path/to/db' AS x` is valid syntax and matches no entry in the blocklist. Whether the agent rejects this via the statement-type allowlist depends on how the configured `sqlglot` dialect parses it; on PostgreSQL dialect parsing fails (sqlglot returns no `Select`) and the statement-type check rejects, but a SQLite-dialect SQLChatAgent (`database_uri="sqlite:///..."`) returns the statement as `sqlglot_exp.Attach`, whi
CVSS v4.0
Affected software
Run on your own infrastructure? Check whether these packages are installed with threat-finder — our free open-source scanner.
AI-Powered Analysis
Machine-generated threat intelligence
Technical Analysis
The SQLChatAgent in langroid uses a regex-based blocklist (_DANGEROUS_SQL_PATTERNS) to prevent dangerous SQL operations by blocking specific function names. However, this blocklist omits several PostgreSQL filesystem-disclosure functions such as pg_read_file(), pg_stat_file(), pg_ls_logdir(), pg_ls_waldir(), and pg_current_logfile(), which return file contents or directory listings. These functions are callable via SELECT statements, which pass the agent's statement-type allowlist and evade the regex blocklist, reaching the SQLAlchemy execution engine. Consequently, an attacker able to influence the LLM's generated SQL query string can read arbitrary files on the PostgreSQL host without needing database credentials. Similar gaps exist for SQLite's ATTACH without the DATABASE keyword and SQL Server's OPENDATASOURCE. The affected versions are langroid <= 0.63.0. No patch or official fix is currently available.
Potential Impact
An attacker who can influence the SQL queries generated by the LLM in langroid's SQLChatAgent can read arbitrary files from the PostgreSQL host filesystem. This can lead to unauthorized disclosure of sensitive data stored on the database server. The attack requires no direct database credentials from the attacker, as the agent holds them. The vulnerability bypasses existing query type restrictions and regex-based blocklists, making it a high-severity information disclosure risk.
Mitigation Recommendations
Patch status is not yet confirmed — check the vendor advisory for current remediation guidance. Until an official fix is released, restrict or sanitize inputs that influence the LLM-generated SQL queries to prevent injection of dangerous function calls. Consider disabling or limiting the use of SQLChatAgent features that allow arbitrary query execution, or deploy additional query validation layers that explicitly block the missing PostgreSQL functions (e.g., pg_read_file, pg_stat_file, pg_ls_logdir, pg_ls_waldir, pg_current_logfile).
Technical Details
- Gcve Source
- db.gcve.eu
- Osv Id
- GHSA-pmch-g965-grmr
- Osv Schema Version
- 1.4.0
- Aliases
- ["CVE-2026-50180"]
- Ecosystems
- ["PyPI"]
- Database Specific Severity
- HIGH
- Cvss Version
- 4.0
Threat ID: 6a46ecc427e9c7971943d784
Added to database: 07/02/2026, 22:57:08 UTC
Last enriched: 07/02/2026, 23:17:30 UTC
Last updated: 07/31/2026, 19:22:59 UTC
Views: 48
Community Reviews
0 reviewsCrowdsource mitigation strategies, share intel context, and vote on the most helpful responses. Sign in to add your voice and help keep defenders ahead.
Want to contribute mitigation steps or threat intel context? Sign in or create an account to join the community discussion.
Actions
Updates to AI analysis require Pro Console access. Upgrade inside Console → Billing.
Need more coverage?
Upgrade to Pro Console for AI refresh and higher limits.
For incident response and remediation, OffSeq services can help resolve threats faster.
Latest Threats
Check if your credentials are on the dark web
Instant breach scanning across billions of leaked records. Free tier available.