VDB
EN
CRITICAL

GHSA-6xc5-4r68-67fc

Langroid: SQLChatAgent dangerous-function blocklist can be bypassed with quoted or schema-qualified pg_read_file calls

상세

# SQLChatAgent `_validate_query` dangerous-pattern regex is bypassable via quoted/commented/qualified function names

## Summary

The `SQLChatAgent` SQL-injection mitigation, with default `allow_dangerous_operations=False`, combines a raw-text regex blocklist (`_DANGEROUS_SQL_PATTERNS`) with a `sqlglot` SELECT-only statement allowlist. The blocklist entries that target callable functions require the function name to be immediately followed by `\s*\(`.

PostgreSQL accepts the same call with the name separated from `(` by a quoted identifier, an inline comment, or schema qualification. These forms evade the regex, still parse as `SELECT`, and execute the same PostgreSQL function. This restores the `pg_read_file` server-side file-read primitive that the prior CVE-2026-25879 / GHSA-pmch-g965-grmr fix was meant to block: the parent advisory fixed a missing `pg_read_file` blocklist entry, while this report shows that the added regex is bypassable.

## Affected Code

Tested against current `main` commit:

`6e8e7b2bb23ec04c1c25be479f16b8cc9a4f8796`

The current source still contains:

```python re.compile(r"\bpg_(read|stat|ls|current_logfile)[A-Za-z0-9_]*\s*\(", re.IGNORECASE) ```

`_validate_query` checks the raw query against `_DANGEROUS_SQL_PATTERNS`, then parses with `sqlglot` and allows `SELECT` statements. The dangerous-call check is raw text, not normalized AST function-name matching.

## Root Cause

The current mitigation treats dangerous PostgreSQL function calls as a raw-text regex problem. The regex requires the `pg_...` function token to be followed directly by optional whitespace and `(`, but PostgreSQL accepts equivalent calls through quoted identifiers, comments, and schema-qualified names. Because `_validate_query` only uses `sqlglot` to enforce the top-level statement type, those normalized function names are never checked after parsing.

## Auth Boundary

The boundary is the default `SQLChatAgent` safety policy between attacker-influenced SQL generation and database operations that can read server-side files. With `allow_dangerous_operations=False`, a user or prompt that influences generated SQL should not be able to bypass the guard and execute PostgreSQL file-read functions such as `pg_read_file`.

This is not a new unauthenticated endpoint or product-wide SQL injection; it applies when untrusted user content can influence SQLChatAgent's generated SQL.

## Reproduction

The local harness uses the current `sql_chat_agent.py`, extracts the real shipped dangerous regex list, validates the queries with real `sqlglot==30.8.0`, then executes the accepted bypasses against a local throwaway PostgreSQL 16 container.

Transcript excerpt:

```text CONTROL "SELECT pg_read_file('/etc/passwd')" -> REJECTED: matches '\\bpg_(read|stat|ls|current_logfile)[A-Za-z0-9_]*\\s*\\(' BYPASS 'SELECT "pg_read_file"(\'/etc/passwd\')' -> ALLOWED (validator returned None -> would execute) BYPASS "SELECT pg_read_file/**/('/etc/passwd')" -> ALLOWED (validator returned None -> would execute) BYPASS 'SELECT pg_catalog."pg_read_file"(\'/etc/passwd\')' -> ALLOWED (validator returned None -> would execute)

=== Part B: real PostgreSQL execution of the bypass === connected; is_superuser=t executed bypass 'SELECT "pg_read_file"(\'<file>\')' -> file contents returned: 'LANGROID_SAFE_MARKER_...' executed bypass "SELECT pg_read_file/**/('<file>')" -> file contents returned: 'LANGROID_SAFE_MARKER_...' executed bypass 'SELECT pg_catalog."pg_read_file"(\'<file>\')' -> file contents returned: 'LANGROID_SAFE_MARKER_...'

RESULT: VULNERABLE ```

The control query is blocked by the current regex, while all three equivalent PostgreSQL forms are allowed by the validator and return the mounted proof file contents from a real PostgreSQL server. The `LANGROID_SAFE_MARKER_...` value is a harmless marker generated inside the throwaway local container for this proof.

## Impact

On a deployment using `SQLChatAgent` against PostgreSQL with a role able to call `pg_read_file` (superuser, or a role granted `pg_read_server_files`), an attacker who can influence LLM-generated SQL can coerce the agent into emitting one of the obfuscated queries and read files accessible to the PostgreSQL server process through `pg_read_file`.

This is the same impact and precondition shape as the published `pg_read_file` advisory, but it targets the bypassability of the current regex-based fix rather than the pre-fix absence of a `pg_read_file` block.

Severity: High by parity with the published parent advisory; not Critical. CWE-184 leading to server-side file read.

## Suggested Fix

Do not rely on raw-text regex matching for dangerous-call detection. After the existing `sqlglot` parse, walk the AST and reject any function invocation whose normalized, unquoted, schema-stripped, case-folded name is in a dangerous set such as `pg_read_file`, `pg_read_binary_file`, `pg_ls_dir`, `pg_stat_file`, `lo_import`, `lo_export`, `load_file`, or `load_extension`.

Also recommend running SQLChatAgent with a least-privilege database role that lacks `pg_read_server_files`.

이 버전이 영향받나요?

사용 중인 패키지 버전을 입력하면 즉시 평가합니다.

영향 패키지

PyPI / langroid
최초 영향 버전: 0 수정 버전: 0.65.1
수정 pip install --upgrade 'langroid>=0.65.1'

참고