Quick Take
- What: SQL injection in the
count_rowstool’swhereparameter allowed subqueries against unintended tables - Who is affected: pgEdge Postgres MCP Server 1.0.0 and earlier, including all alpha and beta pre-releases
- What to do: Upgrade to version 1.1.0 or later
- Scope: Attackers could read any table the connected database role had SELECT privileges on, but could not write or escalate privileges
- Workaround: Grant the connecting role SELECT only on tables you intend to expose
In plain English
The pgEdge Postgres MCP Server is a tool that lets AI assistants query PostgreSQL databases through the Model Context Protocol. A security flaw in its count_rows feature let someone ask how many rows matched a condition, but they could sneak in a hidden query about a completely different table. By checking whether the count was zero or not, an attacker could extract data from any table the database user could read, one character at a time. The flaw is fixed in version 1.1.0. If you use this tool, upgrade now. If you don’t use pgEdge Postgres MCP Server, you can ignore this.
What happened
The count_rows tool in pgEdge Postgres MCP Server accepted a where parameter to filter rows. This parameter was inserted directly into a SQL SELECT COUNT(*) statement with no validation beyond a generic read-only check. According to the security advisory, a caller could supply a subquery in the where clause referencing a table that was never named in the tool call.
For example, calling count_rows(table="orders", where=(SELECT count(*) FROM customers WHERE email = 'guess') = 1) would return the full orders count when the guess was correct and zero when it was wrong. This let an attacker enumerate arbitrary data in a table they never named, using the returned count as a true/false signal.
The advisory states that both boolean-blind and error-based blind injection techniques were confirmed exploitable. The flaw was reproduced directly against the tool’s handler and verified against a live PostgreSQL 16 instance.
The database privilege boundary remained intact. The advisory confirms that attempts to read tables the connected role had no grant on failed with “permission denied for table” errors, and the flaw could not write data or escalate beyond the connected role’s existing SELECT privileges. Classic stacked-query injection (e.g., ; DROP TABLE ...) was already correctly rejected by existing guards.
What you should do
Upgrade to pgEdge Postgres MCP Server version 1.1.0 or later. The fix rejects any where clause containing a bare SELECT or TABLE keyword in the comment-stripped, literal-blanked residue. According to the commit message, the fix was verified by reproducing every payload from the report against a live server before the change and confirming all were rejected afterwards.
As a defense-in-depth measure, grant the connecting database role SELECT privileges only on the tables you intend to expose through the MCP server. This limits what an attacker could reach even if a similar flaw were discovered in the future.
What it means
This vulnerability is a reminder that AI agent tools that construct SQL queries from user input must validate those inputs carefully. Even when a tool appears to accept only a filter predicate, any string interpolation into SQL can become an injection vector if subqueries or other SQL constructs are not explicitly blocked.
The pgEdge team’s fix takes a surgical approach, rejecting subqueries while leaving legitimate simple predicates untouched. The query_database and execute_explain tools were unaffected, since running arbitrary SQL is their documented purpose.
The CVSS score of 6.5 reflects the fact that an attacker needed valid credentials (Privileges Required: Low) and could only read data the connected role already had access to (Confidentiality: High, Integrity: None, Availability: None, Scope: Unchanged).



