Intelligence

The Blocklist Was AWS's Own. It Did Not List Every Way Past Read Only.

CVE-2026-85787, published 4 September 2026 as GHSA-pwr4-hmph-gqgc, describes AWS Labs' own postgres-mcp-server enforcing its read only mode through a keyword and function blocklist that, in versions before 1.1.7, left out enough disallowed input that content an authenticated user submitted could modify data beyond the read only scope the server was configured to hold it to. Unlike Crystal DBA's Postgres MCP Pro, where a companion record on this desk independently ran the affected validator and reproduced the exact traversal gap, AWS's own advisory does not disclose which specific input the pre 1.1.7 blocklist failed to cover, and no source at that exact vulnerable version was available to this session to test directly. What this record did confirm directly: the corrected version, 1.1.7, shipped 25 June 2026, more than two months before the advisory's own 4 September 2026 publication date, five further versions have shipped since, through 1.2.0 on 26 August 2026, and the project's own current documentation states plainly that its blocklist is a best effort, defense in depth mechanism, not a security boundary, recommending a dedicated, minimal privilege PostgreSQL role as the control that actually holds regardless of what SQL reaches it.

Event analysed: . This analysis was published on 7 September 2026.

AWS Labs' own postgres-mcp-server already blocked write statements by keyword when read only mode was on. What did versions before 1.1.7 still let an authenticated user's submitted content do, and does a shipped fix from two months before the advisory close the question of what was missed?

It closes the vulnerable version, not the mechanism. GitHub Security Advisory GHSA-pwr4-hmph-gqgc formally assigns CVE-2026-85787 on 4 September 2026, CWE-184, Incomplete List of Disallowed Inputs, CVSS 3.1 base score 6.5, Moderate, vector AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N, against awslabs.postgres-mcp-server before version 1.1.7, stating that an incomplete list of disallowed inputs in the SQL validation component might allow content submitted during an authenticated user's own MCP interaction to modify data beyond the intended read only scope. This record fetched that advisory directly and confirms those facts as the advisory itself states them. What the advisory does not state, and what this record could not independently establish, is which specific input the pre 1.1.7 blocklist omitted: no historical source at that exact vulnerable version was available to this session, and AWS's own advisory names no proof of concept. This record fetched the project's own PyPI release history directly and confirmed the corrected version, 1.1.7, published 25 June 2026, more than two months before the advisory's own 4 September 2026 publication date, with five further releases shipped since, the current latest being 1.2.0 on 26 August 2026, so no supported deployment tracking the project's own releases remains on a vulnerable version as of this record. This record also fetched the project's own current README directly, which states its read only enforcement as a keyword and function blocklist, DML and DDL verbs, session state statements, anonymous code blocks, and named high impact functions, and states in its own words that this blocklist is a best effort, defense in depth mechanism, not a security boundary, naming obfuscation, quoted identifiers and new server or extension functions as categories a blocklist cannot enumerate in full, and recommends a dedicated, minimal privilege PostgreSQL role, explicitly not a superuser, rds_superuser or the cluster master user, as the control that holds regardless of what SQL reaches the server. That is the same distinction a companion record on this desk already established by directly running Crystal DBA's Postgres MCP Pro validator against both forms of an identical function call: a keyword or name check that exists and is correctly enforced against the representations its own author tested is not the same guarantee as coverage of every representation or every input the underlying grammar can produce, and this record treats AWS's own advisory as a second, independently arising instance of that same gap between a declared read only mode and the resource authority it actually delivers, without asserting the two vendors' underlying mechanisms are identical, since this record could not verify AWS's own mechanism to the same depth.

Two vendors, two months apart, published an advisory admitting the same category of gap in a PostgreSQL MCP server's own read only mode. Crystal DBA's came with a reporter's proof of concept this desk could run directly, an open pull request this desk could apply and test, and an unpatched tagged release this desk could confirm still carried the bug. AWS Labs' advisory came with none of that: a corrected version already two months old, a vendor description that names the CWE class without naming the input, and a current README that states, in AWS's own words, why a blocklist was never meant to be trusted alone. This record treats that difference in what is knowable as itself part of what it reports, rather than filling the gap with an inferred mechanism borrowed from the other vendor's own, separately documented bypass.

What the advisory itself states

GitHub Security Advisory GHSA-pwr4-hmph-gqgc, fetched directly by this session, formally assigns CVE-2026-85787 on 4 September 2026, CWE-184, Incomplete List of Disallowed Inputs, at a CVSS 3.1 base score of 6.5, Moderate, vector AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N, against the PyPI package awslabs.postgres-mcp-server, versions before 1.1.7, corrected in 1.1.7. Its own description states that an incomplete list of disallowed inputs in the SQL validation component could allow an authenticated user's own submitted content to modify data beyond the read only scope the server was intended to hold that interaction to, and its own mitigation guidance recommends connecting through a dedicated, minimal privilege PostgreSQL role rather than a superuser account. The advisory carries no proof of concept, no linked commit and no linked pull request that this session's own fetch of the page returned, and its listed contact is aws-security@amazon.com.

What this record could and could not independently confirm: the advisory's own stated facts, the CVE identifier, the CWE class, the CVSS vector, the affected and corrected version boundary, and the published date, all fetched and read directly rather than taken from a summary. Not confirmed, because no source exists that this session could reach: which specific keyword, function, statement shape or encoding the pre 1.1.7 blocklist omitted. This record states that gap plainly rather than inferring a mechanism the available evidence does not establish.

A shipped fix, read against its own timeline

This record fetched the project's own PyPI release history directly. Version 1.1.6 published 3 June 2026. Version 1.1.7, the version GHSA-pwr4-hmph-gqgc names as corrected, published 25 June 2026, twenty two days later and more than two months before the advisory's own 4 September 2026 publication date. Five further releases have shipped since: 1.1.8 on 22 July 2026, 1.1.9 on 28 July 2026, 1.1.10 on 5 August 2026, 1.1.11 on 10 August 2026, and 1.2.0 on 26 August 2026, the project's own current latest version at the time of this record, itself adding a further least privilege connection guardrail according to the project's own release history. Every one of these five releases postdates the fix this advisory names. This record treats that timeline as establishing only what it establishes: a deployment tracking the project's own released versions at any point after 25 June 2026 was not running the code this advisory describes as vulnerable, a materially different lifecycle position than Crystal DBA's own Postgres MCP Pro, where this desk separately confirmed the identical gap it ran against a tagged release also reproduces, unpatched, against that project's current default branch.

The vendor's own documentation already states the boundary this bypass crossed

This record fetched the project's own current README directly rather than relying on the advisory's own summary of what the server does. Its own wording, quoted directly: when the MCP server runs without the operator supplied allow write query flag, it rejects queries that appear to mutate data or session state, implemented with a keyword and function blocklist, DML and DDL verbs such as INSERT, UPDATE and DROP, session state statements such as SET, RESET, DISCARD and LOAD, anonymous code blocks, and high impact functions including pg_terminate_backend, pg_sleep and the advisory lock family. The same document, in its own words, states the limitation directly: treat this as a best effort, defense in depth mechanism, not a security boundary, naming obfuscation, quoted identifiers and new server or extension functions as categories a blocklist cannot enumerate in full, and states plainly, do not rely on it as your only control. Its own recommended control is a dedicated Postgres role holding only the privileges it needs, explicitly not a superuser, rds_superuser or the cluster master user, so that the database itself enforces the boundary regardless of what SQL reaches it. This record did not have to infer this boundary from the advisory. The vendor's own current documentation already states it, in language this record confirms was fetched directly rather than paraphrased from a third party summary.

Why this record connects to, rather than duplicates, Crystal DBA's own occurrence

A companion record on this desk already ran Crystal DBA's own Postgres MCP Pro validator directly against a function name blocked in one syntactic position and permitted in an equivalent one, confirming precisely how a check that exists and correctly enforces its own allow list against a FuncCall node reached directly never runs at all against the identical call nested inside a RangeFunction node's own functions attribute. That record grounds the canonical weakness this dataset already carries as representation dependent authorization, an operation blocked in one syntactic form authorized in an equivalent one: a check that works, demonstrated against at least one representation, left unreached by an equally valid alternate representation of the identical operation. This record does not claim AWS's own advisory demonstrates that same specific mechanism. CWE-184, Incomplete List of Disallowed Inputs, is a broader class than a single traversal gap in one parser's node taxonomy, and could equally describe a keyword the list never included at all, a construct the same class this record's own read of the current README names directly as the blocklist's own acknowledged limitation, obfuscation, quoted identifiers, or a new server or extension function. What this record does assert, because two independently maintained implementations now each state it in their own words, is the broader property both occurrences instantiate: a declared read only mode, enforced by a name or keyword check against SQL a parser has already produced, is only as complete as the set of representations and inputs that check was built and tested to recognize, and AWS's own current documentation states that limitation about its own blocklist more plainly than most vendors state it about their own security controls at all.

What this record deliberately did not chase

A separate, older AWS Labs postgres-mcp-server issue, GitHub issue 911, opened 22 July 2025 and since closed, reports the opposite defect, an operator supplied readonly false argument still being refused as though read only were forced on. This record read that issue directly, confirmed it concerns a configuration flag failing to permit writes rather than a validation gap failing to block them, and excludes it from this occurrence as unrelated, a distinct bug filed against an earlier version and a different failure direction entirely.

The Authority Provenance ledger

Authority grantor. The operator who deploys awslabs.postgres-mcp-server, omits the allow write query flag specifically to hold an agent to read only interaction, and configures the PostgreSQL role the server authenticates as.

Caller identity. An authenticated user interacting with the MCP server, per the advisory's own description; the advisory names no further detail about which MCP client or agent context this record can independently confirm.

Caller mandate. Read only interaction with the connected PostgreSQL database, the entire reason the operator ran the server without the write query flag rather than granting broader authority outright.

Intermediary. The server's own SQL validation component, described by the advisory and confirmed directly by this record's own read of the current README as a keyword and function blocklist evaluated before a query reaches PostgreSQL.

Intermediary credential. The PostgreSQL role the server's connection authenticates as, whatever privileges that role separately holds, exactly the boundary the project's own current documentation names as its own recommended, database enforced control.

Downstream target. Data within the intended read only scope, per the advisory's own description of the consequence, modified beyond that scope by content an authenticated user submitted.

Failed limit. Named by AWS on 9 September 2026, and no longer unknown to this record. CVE-2026-87911, GHSA-fph8-pg5w-78fv and AWS Security Bulletin 2026-104-AWS each state, in AWS's own words, that the pre 1.1.7 SQL validation component did not block all SQL verbs that can perform system level operations in its default read only mode, and name the omitted construct directly: a crafted COPY ... TO PROGRAM statement. The earlier CVE-2026-85787 record's open question about which input the list omitted is answered by the vendor itself, and the stated consequence widens from data modification beyond read only scope to possible operating system command execution on the host of a self managed PostgreSQL server.

Inherited assumption. That the blocklist's own enumeration of DML and DDL verbs, session state statements and named dangerous functions was complete enough to hold the server to read only behavior on its own, an assumption the project's own current documentation now states directly should never have been made, describing the same mechanism as best effort and defense in depth rather than a security boundary.

Credential binding. Not established by available evidence for this specific occurrence; the advisory does not state whether the demonstrated consequence depended on the connected role's own separate privileges, unlike Crystal DBA's own occurrence, where this desk's companion record states that dependency directly.

Destination binding. Absent on whatever input the pre 1.1.7 blocklist failed to enumerate; present and correctly enforced, per the vendor's own documentation, against every keyword, statement and function that same documentation names explicitly.

Challenge authority. None on the MCP layer for whatever input bypassed the blocklist prior to 1.1.7; whatever the connected PostgreSQL role's own privileges separately permit or refuse once a query reaches the server, the same distinct and already correctly enforced boundary the project's own documentation names as its recommended control.

Revocation or modification. Shipped. Version 1.1.7 published 25 June 2026, confirmed directly against the project's own PyPI release history, more than two months before the advisory's own publication date, with five further releases since.

Recovery path. Upgrade to 1.1.7 or later, per the advisory's own stated remediation, independently confirmed current at 1.2.0, released 26 August 2026, as of this record; combined, per the project's own current documentation, with connecting as a dedicated, minimal privilege PostgreSQL role rather than a superuser, rds_superuser or the cluster master user.

Exploitation. Unknown. No source available to this record states this was exploited against a production deployment, and the advisory itself carries no proof of concept.

Provenance evidence quality. Strong for the advisory's own stated facts and for the fix timeline: this record fetched the GHSA advisory, the project's own current README and its own PyPI release history directly, rather than through a third party summary. Weaker, by the evidence's own nature rather than by any gap in this session's own effort, for the underlying mechanism: AWS's own advisory names no proof of concept and no specific omitted input, and no source at the exact pre 1.1.7 vulnerable version was reachable to this session, so this record cannot state the mechanism to the depth its companion record achieved for Crystal DBA's own, separately reproduced occurrence. Direct fetch of aws.amazon.com's own security bulletin for this CVE and of awslabs.github.io's own hosted documentation were both blocked by this session's network egress policy on every attempt; this record relies on the GitHub Security Advisory Database's own record and on the project's own source repository instead, both fetched directly.

Update, 10 September 2026: AWS names the omitted input, and it reaches the operating system

The open question this record published on 7 September 2026, which specific input the pre 1.1.7 blocklist failed to enumerate, is now answered by the vendor itself. On 9 September 2026 AWS published Security Bulletin 2026-104-AWS and GitHub Security Advisory GHSA-fph8-pg5w-78fv, assigning CVE-2026-87911 against the same package and the same affected boundary, versions before 1.1.7, corrected in 1.1.7. This session fetched both pages directly, HTTP 200 in each case, and read them rather than a third party summary. Their shared description states that an OS command injection weakness in the read only enforcement of the SQL validation component in awslabs.postgres-mcp-server before 1.1.7 might allow an unauthenticated actor to execute operating system commands on the host of a self managed PostgreSQL server by placing a crafted COPY ... TO PROGRAM statement into content that is processed when an authenticated user interacts with the MCP server in its default read only mode. The advisory's own Impact section states plainly that the component does not block all SQL verbs that can perform system level operations in that default mode.

Two things change materially, and this record states both as the sources state them. First, the mechanism is no longer unknown: the omitted input is a named PostgreSQL construct, COPY ... TO PROGRAM, which the blocklist's own enumeration of DML and DDL verbs, session statements and named functions did not cover, exactly the enumeration limit the project's own README already called best effort rather than a security boundary. Second, the established effect widens. CVE-2026-85787 described modification of data beyond the read only scope, integrity only, at CVSS 3.1 base score 6.5, Moderate, scope unchanged. CVE-2026-87911 is scored by the same advisory page at CVSS 3.1 base 9.6, Critical, vector AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H, with a changed scope and high confidentiality, integrity and availability impact, and carries two weaknesses, CWE-78, OS command injection, alongside the CWE-184 incomplete disallowed input list the earlier CVE carried alone. The consequence class moves from inside the database to the host running it.

The preconditions are stated by AWS and this record repeats them without widening them. The path applies to a self managed PostgreSQL deployment profile reached through the PG_WIRE_PROTOCOL connection method, where the configured database role holds superuser or pg_execute_server_program. AWS states directly that users who connect the MCP server with a minimal privilege database role are not affected, because the database itself would deny the operation regardless of the application layer bypass. That is the same database enforced control the project's own README already recommended and this record already reported, now confirmed by the vendor as the boundary that actually holds when the application layer one does not. AWS credits Mordehai Attia of Corsen AI and Ryan von Brubeck through coordinated disclosure. The corrected version remains 1.1.7, published 25 June 2026, so the fix timeline this record established for the earlier CVE covers this one too.

Still unknown after this update, and deliberately not asserted: whether the operating system command execution has been realized end to end outside a disclosure setting, since neither AWS page carries a proof of concept or a reproduction this session could run; and whether any real deployment has been exploited in the wild, which no source available to this record states. The required privilege condition is no longer unknown, because AWS names it, but what proportion of deployments actually satisfy it is not established by anything this record read.

Where this sits in the pattern

Crystal DBA's own Postgres MCP Pro already showed a function name allow list correctly enforced against one syntactic representation of a call and never reached against an equivalent one, a mechanism this desk independently ran and confirmed. AWS Labs' own postgres-mcp-server, an unrelated codebase maintained by a different organization, now independently states in its own advisory and its own current documentation that its comparable blocklist based read only enforcement carries the same shaped limitation in general, without this record being able to confirm the same specific traversal mechanism drives it. This desk's wider argument, that execution authority has to be evaluated against the action a system actually takes rather than assumed from an earlier, adjacent declaration, extends here to a declared mode a server's own operator chose specifically to narrow what an agent could do through it: read only described only the dimension AWS Labs' own blocklist was built to enforce, and this occurrence, independently arising at a second vendor, is further evidence for a durable property this dataset already carries rather than grounds for a new one, that a policy which correctly blocks the representations and inputs it was tested against is not, on that basis alone, a policy that has been tested against every representation or every input the same effect can be reached through.

What this record does not establish

This record does not claim CVE-2026-85787 has been exploited against a real awslabs.postgres-mcp-server deployment, that its underlying mechanism matches Crystal DBA's own RangeFunction traversal gap, that every deployment connects as a PostgreSQL role holding privileges beyond what the connecting operation itself required, that the project's own blocklist offers no protective value, or that a deployment still pinned to a version before 1.1.7 is not exposed. Where the evidence available to this record does not establish a fact, this record states it as unknown rather than inferring it from the pattern a different vendor's own, separately reproduced occurrence already established.

Sources

This analysis interprets third-party reporting, research and announcements. Moona is not the original reporter of the underlying events.

[2]
AWS Security Bulletin 2026-104-AWS (CVE-2026-87911)
Amazon Web Services (Security Bulletins) · 9 September 2026 · Company announcement
[4]
AWS Security Bulletin 2026-101-AWS
Amazon Web Services (Security Bulletins) · 4 September 2026 · Company announcement
[5]
[6]
mutable_sql_detector.py on the project's current default branch
awslabs/mcp (GitHub, source) · Primary source
[7]
awslabs.postgres-mcp-server release history
Python Package Index (PyPI) · Technical documentation
[8]
awslabs.postgres-mcp-server: --readonly flag not honored (unrelated prior issue, excluded)
awslabs/mcp (GitHub Issues) · 22 July 2025 · Primary source

Protocol evidence

This record does not assess these architectures. The connection runs through the Risk Registry requirement each one bears on, and these published authority architectures are what the evidence says about that requirement.

Protocol evidence related through AEW-020 An operation blocked in one syntactic form is authorized in an equivalent one

  • Supports requirement

    Agent Control Standard (ACS)

    OWASP GenAI Security Project, originally Zenity

    Requirement The specification does not guarantee complete mediation of every consequential action a host can take

    ACS's own conformance document states directly that the specification does not guarantee complete mediation of every consequential action a host can take, and that a primitive bypassing its own toolCallRequest hook is invisible to policy. Postgres MCP Pro's restricted mode is concrete evidence for exactly the concern that admission names in the abstract: a mediation point that is present, correctly implemented and independently confirmed to work against one representation of a consequential operation still does not guarantee coverage of every representation the same underlying grammar can produce for it. This entry's own execution of the real, patched and unpatched validator against the same four functions supports the requirement that policy coverage be verified against the full range of representations a parser can produce, not only the one a mediation point's own author tested against. MCPHub's isBlockedIpv6 is the same admission read against a destination guard rather than a tool call hook: this entry's own direct reading of the pre fix and post fix source confirms a mediation point that correctly and completely covered every address form its own test suite exercised still left three standardized transition encodings of the same prohibited resource unmediated, supporting the requirement that coverage be verified against every representation an addressing standard can produce, not only the ones a guard's own author tested against.

    View protocol evidence

  • Supports requirement

    Verifiable Attenuated Delegation for AI Agent Chains (draft-asor-wimse-agent-delegation-chain)

    Rafael Asor, Attenu

    Requirement A verifier encountering an unrecognized constraint type must deny rather than treat it as unconstrained

    This draft's own verified requirement is that a verifier encountering an unrecognized constraint type must deny rather than treat it as unconstrained, so that a newer, unrecognized restriction is never silently read as no restriction at all. Postgres MCP Pro's SafeSqlDriver shows the inverse failure at a different layer, its own recursive AST traversal encountering a container it does not open, the tuple nested inside RangeFunction's own functions attribute, and treating what it never inspected as though it carried nothing requiring a check, rather than denying by default. This entry's own execution of the real validator confirms the same allow list check works correctly wherever the traversal does open the container it needs to, which is exactly why an unopened one silently passing is the more precise finding here, direct evidence for why a verifier's default posture toward a structure it does not fully parse matters generally, not only for this one draft's own constraint objects. MCPHub's isBlockedIpv6 supports the same requirement at a resource identity boundary rather than a syntax tree boundary: an IPv6 address encoded through the NAT64, 6to4 or Teredo transition prefixes was an unrecognized representation isBlockedIpv6 silently treated as unconstrained, returning false rather than denying by default, until the fix taught the function to recognize those three encodings as the blocked addresses they carry.

    View protocol evidence

Related Intelligence

All Intelligence Records →