Intelligence
AEV-2026-0028

Postgres MCP Pro restricted mode allow list does not reach a function called in a FROM clause (CVE-2026-85620)

Postgres MCP Pro's SafeSqlDriver blocks a dangerous PostgreSQL function by name whenever pglast represents the call as a FuncCall node, and this entry confirmed that block directly by running the real validator. The identical function called inside a FROM clause parses as a RangeFunction node whose functions attribute nests the same FuncCall one further tuple deep, a container the validator's own recursive walk never opens, so the same allow list check never runs. Formally assigned CVE-2026-85620 on 4 September 2026, with a proposed fix, pull request 200, open and unmerged as of this entry.

AESS 7.7 highConfirmedStatus: publishedEvent: 4 September 2026Execution Authority

Affected

Organisation
The crystaldba postgres-mcp project (Crystal DBA)
Product
Postgres MCP Pro
Component
SafeSqlDriver._validate_node and _validate in src/postgres_mcp/sql/safe_sql.py, exercised only when the server is run in restricted access mode
Versions
Through version 0.3.0, confirmed directly by executing the affected validator at the v0.3.0 tag; independently confirmed still present, unpatched, at the project's current default branch head, commit 15c8e33353546148acc2d8bd784551cf3905d1e2; no patched version found
Configurations
Server started with --access-mode=restricted, confirmed directly against server.py, which wraps the base SQL driver in SafeSqlDriver only under this flag; The connected PostgreSQL role holds the pg_read_server_files privilege or superuser status, the precondition the primary issue states for the file read functions to succeed once the query reaches PostgreSQL

Execution authority facts

Fields the evidence does not establish are shown as unknown rather than guessed.

Intended task
An MCP client or agent runs a SQL query through Postgres MCP Pro's restricted mode, which is intended to limit execution to an explicit allow list of function calls
Agent
Postgres MCP Pro's own SafeSqlDriver, which parses and validates a query before handing it to PostgreSQL
Delegating actor
The operator who deploys Postgres MCP Pro, selects restricted mode specifically to constrain what a connected agent may execute, and configures the PostgreSQL role the connection authenticates as
Action
Executed pg_read_file, pg_ls_dir, pg_read_binary_file or pg_stat_file called inside a FROM clause, immediately after this entry confirmed the identical call in ordinary expression position is correctly rejected by the same validator
Target resource
Files on the PostgreSQL server's own filesystem reachable by the process, gated only by whatever the connected role's own pg_read_server_files or superuser privilege separately permits
Environment
Wherever the operator deploys Postgres MCP Pro and its connected PostgreSQL server
Credentials used
The PostgreSQL role the restricted mode connection authenticates as
Privileges available
Whatever server side file access that role's own pg_read_server_files or superuser privilege grants once a query reaches PostgreSQL itself
Authority presented
A syntactically valid SQL query naming an otherwise disallowed function inside a FROM clause rather than in the ordinary expression position the same validator already checks
Authority required
Authorization for the specific function invoked, independent of which grammar position within the query the function name appears in
Applicable policy
SafeSqlDriver's ALLOWED_FUNCTIONS check, confirmed directly to correctly block pg_read_file, pg_ls_dir, pg_read_binary_file and pg_stat_file when pglast represents the call as a FuncCall node reached by the validator's own recursive walk; not reached when the identical FuncCall is nested inside a RangeFunction node's own functions attribute
Approval mechanism
none
Required approver
unknown
Independent approval
no
Action binding
The allow list check is bound to a FuncCall node the recursive walk visits directly; it is not bound to the semantically identical FuncCall this entry confirmed sits one tuple deeper inside a RangeFunction's own functions attribute
Sequence context
Validation runs once, inside _validate, immediately before execute_query hands the query to PostgreSQL with force_readonly forced to true; there is no later checkpoint that re-examines which functions the query actually invokes

Impact

Consequence
File contents returned by pg_read_file, pg_ls_dir, pg_read_binary_file and pg_stat_file called through the bypass path, confirmed directly by this entry running the real, unmodified SafeSqlDriver from the affected v0.3.0 source against all four functions, with a CREATE TABLE write control confirmed still blocked throughout
Reach
organisational
Reversibility
unknown
Detectability
unknown
Propagation
A concrete propagation path exists, for example reading a credential bearing file the PostgreSQL server process can access, but this entry did not observe the effect actually spreading to a further system
Recovery
No patched version found; v0.3.0 remains the project's latest tag as of this entry. Pull request 200 proposes the missing traversal step; this entry applied and ran that exact diff and confirmed it blocks the bypass while still permitting an allow listed FROM clause function, unnest. It is open and has not merged as of this entry.

Evidence

Primary sources

Supporting sources

Reproduction status
Independently reproduced by Moona Intelligence. This session downloaded the real, unmodified SafeSqlDriver source at the v0.3.0 tag and executed its own _validate method directly against the blocked and bypassing forms of four distinct dangerous functions, against unnest as an allow listed control, and against a CREATE TABLE write control, with no PostgreSQL connection required. This session separately parsed the bypass query with the same pglast library the validator itself uses, confirming the exact nested tuple structure inside RangeFunction.functions, and applied and ran pull request 200's own diff against the same source, confirming what it closes and what it leaves unaffected.
Evidence state
Confirmed

Known unknowns

  • Whether this has been exploited against a real Postgres MCP Pro deployment.
  • What share of restricted mode deployments connect as a PostgreSQL role holding pg_read_server_files or superuser privilege, as distinct from a role that would be refused by PostgreSQL's own privilege system once the query reached it.
  • Which body served as the assigning CVE Numbering Authority for CVE-2026-85620; GitHub's own advisory record attributes publication to the National Vulnerability Database, and this session could not independently confirm the assignment chain since direct fetch of both cve.org and nvd.nist.gov was blocked by this session's network egress policy.
  • Whether pull request 200 will merge as currently written or be revised first.

Limitations

  • This session's direct fetch of the CVE Program's own record at cve.org and of nvd.nist.gov were both blocked by this session's network egress policy; CVE metadata is corroborated through the GitHub Advisory Database rather than read directly from either.
  • The GitHub issue and the pull request were also read through this session's automated page fetch and summarization tool, though every material claim taken from them is independently confirmed against this session's own direct execution of the affected source and its own direct parse of the bypass query.

Claim provenance