You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Read-only enforcement bypassed by leading SQL comment — write statements execute #2101
A connection's read-only enforcement (the Forbidden: Connection is read only for external clients guard, hit via the MCP server) classifies a statement as read vs. write by inspecting its leading keyword, but only after trimming leading whitespace — not leading SQL comments. Prefixing any write statement with a /* … */ block comment or a -- … line comment causes it to be misclassified as read-only and executed.
Steps to reproduce
On a read-only connection (via the MCP execute_query):
UPDATE some_table SET id = id WHERE 1=0; → correctly blocked (Forbidden: Connection is read only…).
/* x */ UPDATE some_table SET id = id WHERE 1=0; → executes (not blocked).
-- x⏎UPDATE some_table SET id = id WHERE 1=0; → executes (not blocked).
/* x */ CREATE TEMP TABLE probe (id int); → executes; table is created (confirmed via to_regclass).
For contrast, these are all correctly blocked: plain INSERT/UPDATE/DELETE/CREATE TEMP TABLE, a write hidden in a CTE (WITH x AS (INSERT … RETURNING) SELECT …), and a write with only leading whitespace/newlines.
Expected
A read-only connection rejects any statement that modifies data or schema, regardless of leading comments.
Actual
Leading /* */ or -- comments bypass the check; the write reaches the database.
Likely root cause
Statement-type detection strips leading whitespace but not leading comments before matching the first keyword. A comment shifts the keyword out of the inspected position.
Suggested fix
Strip/skip leading SQL comments (both /* */ and --) before classifying the statement, or classify using a tokenizer/parser rather than a leading-string match. Ideally also enforce read-only at the session/transaction level (e.g. SET default_transaction_read_only = on / a read-only DB session) as defense-in-depth so client-side misclassification can't reach the database.
Environment: PostgreSQL 13; tested via the MCP server with a read-only connection.
Apologies for the noise — this is already fixed. I was on v0.51.1. QueryClassifier.isWriteQuery strips leading SQL comments as of 2478dc55 (first released in v0.55.0), with regression tests covering comment-prefixed INSERT/DELETE. I've since updated to v0.64.0 and confirmed all the comment-prefixed write cases are now correctly blocked. Closing as already-fixed — thanks for the great tool.
Activity
udbhav-si commentedon Aug 13, 2026
Apologies for the noise — this is already fixed. I was on v0.51.1.
QueryClassifier.isWriteQuerystrips leading SQL comments as of2478dc55(first released in v0.55.0), with regression tests covering comment-prefixedINSERT/DELETE. I've since updated to v0.64.0 and confirmed all the comment-prefixed write cases are now correctly blocked. Closing as already-fixed — thanks for the great tool.