CVE-2026-85787: AWS postgres MCP read-only denylist missed set_config()

CVE-2026-85787: AWS postgres MCP read-only denylist missed set_config()

How to fix CVE-2026-85787: upgrade awslabs.postgres-mcp-server to 1.1.7 or newer

3 min read665 words
Contents

Read-only mode on AWS Labs' Postgres MCP server was enforced by a keyword denylist that missed several Postgres primitives. Before awslabs.postgres-mcp-server 1.1.7, an incomplete blocklist in mutable_sql_detector.py could let SQL that rides an authenticated MCP session mutate data or session state even when the server was configured for read-only queries. AWS published this as CVE-2026-85787 / bulletin 2026-101-AWS on 2026-09-04.

Upgrade to awslabs.postgres-mcp-server 1.1.7 or newer (current PyPI tip is 1.2.0). Pair the bump with a least-privilege Postgres role. The application denylist is defense in depth, not the primary boundary.

What breaks

The MCP server translates natural-language tool calls into SQL against Aurora/Postgres. In the default configuration readonly_query is true unless you pass --allow_write_query. Mutating statements are supposed to die in detect_mutating_keywords / check_sql_injection_risk before they hit the wire.

The 1.1.6 → 1.1.7 patch (PyPI upload 2026-06-25) shows what the old denylist missed. Blocking the SET keyword did not catch the function form set_config(...), because set is not on a word boundary inside set_config. A read query could therefore call set_config('row_security','off',false) or set_config('session_replication_role',...) and defeat RLS-style isolation or session hardening that operators thought the MCP could not touch. The same release also closed related gaps the bulletin only summarizes as an "incomplete list":

  • SET_CONFIG added to mutating keywords (read-only mode) plus a dedicated SECURITY_SET_CONFIG_PATTERN that rejects those two GUCs in both read and write modes.
  • Unconditional blocks on the dblink* family (SSRF from the database backend, including to the instance metadata service at 169.254.169.254).
  • COPY ... TO|FROM PROGRAM blocked even when writes are enabled (shell execution on the DB host when privileges allow).
  • strip_quoted_identifiers() so double-quoted spellings like "pg_sleep" no longer slip past word-boundary regexes.
  • Comment-injection heuristic also evaluated on raw SQL, because normalization folded away a trailing --.

AWS rates the bulletin Important. There is no CVSS in the CVE record or NVD entry yet. The attacker model in the advisory is an unauthenticated actor placing crafted SQL into content that an authenticated user later submits through the MCP client. This is not a direct unauthenticated Postgres wire exploit.

Affected: any PyPI awslabs.postgres-mcp-server version < 1.1.7. Fixed: 1.1.7 (2026-06-25). Current tip at write-up: 1.2.0 (2026-08-26).

Who is not in scope

  • Postgres / Aurora itself. This is the AWS Labs MCP wrapper package, not a database engine CVE.
  • Installs already on 1.1.7 or 1.2.x.
  • Deployments that intentionally run with --allow_write_query and already grant the MCP role the DML it needs. Those operators still want the 1.1.7 denylist hardening for dblink* / COPY ... PROGRAM / security GUCs.
  • Other MCP servers (MySQL, DynamoDB, generic SQL bridges). Only this package's SQL validator.

How to check

From the environment that launches the MCP server, inspect the installed package version:

pip show awslabs.postgres-mcp-server

Read the Version field. Any value below 1.1.7 is in the affected range. If you launch via uvx or a Cursor/VS Code MCP config args list, check that pin the same way (or run pip show inside the same environment the MCP process uses).

How to fix

Upgrade the package, then keep the database role tight.

pip install 'awslabs.postgres-mcp-server>=1.1.7'
# or refresh the uvx pin to @latest / @1.2.0

AWS's workaround (still recommended after the bump): connect the MCP server as a dedicated role, never as superuser / rds_superuser / cluster master. For read-only use, grant only CONNECT + USAGE + SELECT on the schemas the agent needs, and prefer forcing read-only transactions at the role level. The denylist and the role privileges should both hold.

What this is not

This is not a remote unauthenticated Postgres login bug, not a claim of active exploitation, and not a same-day emergency for fleets already on 1.1.7+. The CVE assignment landed on 2026-09-04 for a fix that shipped on PyPI on 2026-06-25. Damage is "read-only" MCP sessions that could still change data or session GUCs when the denylist missed a primitive and the DB role was over-privileged.

References

Continue reading

All posts