Repository navigation
fix(access): custom SQL cannot write; batches stop at the first failure - #526
Merged
Merged
Conversation
Index Management's custom fragmentation probe runs caller SQL under utility.access, outside the /sql/execute permission gate. Its safety check stripped comments with a regex before testing for writes, and the regex did not know a `/*` inside a string is not a comment: `SELECT '/*', 1 INTO t FROM x -- */` hid its INTO, passed, and created a table on Postgres and SQL Server (MySQL `INTO OUTFILE` wrote a file). The check now takes the dialect and reads the text as the gate does: one statement by sqlStatementCount, a SELECT/WITH by firstKeyword, and every sqlStatementCategories entry `read`. The old check also refused its own Postgres, CockroachDB, YugabyteDB and Redshift templates (a `;` in a comment); every dialect's template is now tested against it. /sql/execute runs every statement and reports each, which the editor wants. Clone Table assumed it stopped: a failed archive rename still ran the new table's CREATE, so "stopped after N" was false and the advised "rename the archive back" failed on the taken name. The endpoint takes `stopOnError`; Clone Table and runAccessSql send it, and runAccessSql names the failed statement and what ran before and did not run after it. Also: - ClickHouse backup literals double backslashes, and a picked system.backups name is unescaped before being quoted again - /schema/table-insight moves to the schema routes that own it (its own 30/min limiter; it shared the DBA utility one) - utility.index.drop says it only governs the Index Management button Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: bb7e2317-4c2c-40df-9942-14f1502fae01) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
These are the fixes from a review of the Access and Utilities features.
1. Index Management custom SQL could write (security)
POST /schema/index-fragmentationruns caller-supplied SQL withutility.accessonly, so it never goes through the/sql/executepermission check.isSafeIndexFragmentationCustomSqlstripped comments with a regex before checking for writes, and that regex didn't know a/*inside a string isn't a comment. Two statements passed the check because of this:SELECT '/*', 1 INTO fox_probe FROM t -- */creates a table on Postgres and SQL Server.INTO OUTFILEwrites a file on a MySQL server.The default roles only give
utility.accessto editors and above, who can already write, so this wasn't an escalation by default. It was one for any customized role matrix, and it skipped Safe Mode either way.The check now takes the dialect and reads the text the same way the
/sql/executecheck does:sqlStatementCountfirstKeywordsqlStatementCategoriesisreadThe old check also rejected its own built-in templates for Postgres, CockroachDB, YugabyteDB and Redshift, because they contain a
;inside a comment. Those work now, and every dialect's template is tested against the check.2. Clone Table and GRANT scripts now stop at the first failure
/sql/executeruns every statement and reports each one, which is what the editor wants. Clone Table assumed it stopped at the first failure. A failed index rename on the archive still ran theCREATEfor the new live table. The error then said "stopped after N", and the advised "rename the archive back" failed because the live name was taken.stopOnErroroption on/sql/execute: it's off by default, so the editor's behaviour is unchanged.runAccessSql(GRANT/REVOKE in Utilities → Database Access).Smaller changes
\as an escape inside a string, so disk and file names now double backslashes. A backup picked fromsystem.backupsis unescaped before it's quoted again./schema/table-insightfeeds the SQL Editor's Data Peek. It moved from the access routes to the schema routes, which ownprobeTableInsight. The path and permission are the same; it now has its own 30-per-minute limit instead of sharing the DBA utility one.utility.index.drop: its description now says it only controls the Index Management button. Anyone with Change schema can still runDROP INDEXin the SQL Editor.Not in this PR
editor.sidebar.utilitiespermission: saved role settings still name it.runAccessSql:splitSqlStatementshas no dialect option. The server check already covers every reading, so this only affects correctness, not security.Verification
npx vitest run: 505 files passed, 6,210 tests passed.cd apps/web && npx tsc --noEmitis clean.stopOnError: loop, route andrunAccessSql, 5 breaks, each caught.🤖 Generated with Claude Code
Note
High Risk
Changes authentication-adjacent custom SQL validation and multi-statement execution semantics for utilities that run DDL/GRANT sequences; incorrect behavior could allow writes or leave databases half-migrated.
Overview
Security: Index Management custom probe SQL is validated with the same dialect-aware tokenizer as
/sql/execute, replacing comment-stripping regexes that letSELECT … INTOand similar writes slip through onutility.accessonly.Execution:
/sql/executegains optionalstopOnError(default off). Clone Table and Database AccessrunAccessSqluse it so ordered scripts halt after the first failure; GRANT/REVOKE errors now name the failing statement and what ran vs. skipped.Other: ClickHouse backup/restore quotes disk and file names with proper backslash escaping.
/schema/table-insightmoves from access to schema routes (same URL andeditor.rungate, dedicated rate limit).utility.index.droppermission text clarifies it only hides the Index Management drop button.Reviewed by Cursor Bugbot for commit 073c56a. Bugbot is set up for automated code reviews on this repo. Configure here.