Skip to the content.

WhoDB: SELECT … INTO passed the read-only and write-approval gates

Advisory: GHSA-pvf2-6h4x-r24j (published 2026-09-30) Package: github.com/clidey/whodb (Go), affected >= 0.88.0, patched in 0.134.0 Weakness: CWE-863, CVSS 3.1 AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N (Medium)

WhoDB is a database client with a CLI and an MCP server. Two of its features decide whether a SQL statement may write. The CLI has a read-only mode, and the MCP server has --confirm-writes, which asks the user before running anything that changes state. Both relied on one answer from core/src/sqlguard/classify.go.

Root cause

The classifier looked at the first keyword. A statement starting with SELECT counted as a read unless one of the words in mutatingKeywords appeared somewhere in it (INSERT, UPDATE, DELETE, CREATE and so on). INTO, OUTFILE and DUMPFILE were not in that list, so these all came back as Mutating=false:

SELECT * INTO evil_copy FROM users                -- PostgreSQL creates and fills a table
SELECT * FROM users INTO OUTFILE '/tmp/dump.txt'  -- MySQL writes a file on the server

The same verdict fed three paths named in the advisory: the CLI read-only check in cli/internal/database/manager.go, the confirmation prompt in cli/pkg/mcp/tools.go, and MCP read-only validation in cli/pkg/mcp/validation.go.

The maintainers’ own testing, written up in the advisory, adds a detail I had not covered. On PostgreSQL the database-enforced read-only transaction added in 0.130.0 still stopped the table creation with SQLSTATE 25006. On MySQL 8.4 it did not, because INTO OUTFILE is a server-side file operation that the transaction mode WhoDB uses does not block. My report only contained a deterministic classifier reproduction, and I said so. The end-to-end writes were confirmed by the maintainers.

Fix

Commit 9de47fb (“update for protected reads”) changes the approach instead of adding three more words to the denylist. The package comment now says that only a bounded read grammar is treated as automatic, and anything outside it, including syntax whose effects cannot be established, is reported as needing approval. The same commit adds read-guard integration tests that go through the MCP execution path, and touches the ClickHouse plugin and the shared connection pool. The fix shipped in 0.134.0.

What I took from it

Thanks to the WhoDB maintainers for reproducing the issue on both databases, widening the affected range to the real one, and documenting the result in the advisory.