Skip to content

fix: refuse EXPLAIN ANALYZE and INTO OUTFILE as writes - #12

Merged
blaipr merged 1 commit into
mainfrom
fix/read-only-guard-bypass
Sep 2, 2026
Merged

blaipr merged 1 commit into
mainfrom
fix/read-only-guard-bypass

Conversation

@blaipr

@blaipr blaipr commented Sep 2, 2026

Copy link
Copy Markdown
Member

What this changes

isReadOnly() no longer judges a statement by its leading keyword alone. EXPLAIN ANALYZE ... (also DESC/DESCRIBE ANALYZE) and SELECT ... INTO OUTFILE / INTO DUMPFILE are now refused by db_query and db:query, like any other write. Plain EXPLAIN stays allowed. docs/safety.md gains a "What counts as read-only" section and docs/tools.md names the two exceptions.

Why

Both start like a read and are not. MySQL 8 executes the statement under EXPLAIN ANALYZE to time it, and accepts an UPDATE or DELETE there, so rows changed while the output looked like a query plan — a write through the read-only tool with no CDMON_ALLOW_WRITES. INTO OUTFILE writes a file on the database host. Both are refused by pattern rather than parsed around, so a string literal containing INTO OUTFILE is refused too; that is a false refusal, which is the cheap direction to be wrong in here. Tests were sabotage-checked against the previous implementation (2 fail, as expected).

Checklist

  • npm test passes
  • npm run typecheck passes
  • Documentation in docs/ updated if behaviour changed
  • CHANGELOG.md updated if the change is user-observable
  • No credentials, hostnames or database names in the diff

@blaipr
blaipr force-pushed the fix/read-only-guard-bypass branch from 62512d2 to 4f6940c Compare September 2, 2026 22:07
@blaipr
blaipr merged commit 02501d3 into main Sep 2, 2026
2 checks passed
@blaipr
blaipr deleted the fix/read-only-guard-bypass branch September 2, 2026 22:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant