Skip to content

fix(sql): read 'a\\' the way PostgreSQL and SQLite do in the splitter, MCP guard and destructive-query check - #1687

Merged
ZhuchkaTriplesix merged 1 commit into
devfrom
fix/sql-splitter-dialect-escapes
Oct 17, 2026
Merged

ZhuchkaTriplesix merged 1 commit into
devfrom
fix/sql-splitter-dialect-escapes

Conversation

@ZhuchkaTriplesix

@ZhuchkaTriplesix ZhuchkaTriplesix commented Oct 17, 2026 •

Copy link
Copy Markdown
Member

Found while checking a review of the lexers: SqlStatementSplitter had its own copy of the quote/comment loop and always treated \' as an escaped quote (MySQL). SqlLexicalScanner has been dialect-aware since #1566. In PostgreSQL and SQLite a backslash is an ordinary character, so for

SELECT 'a\'; SELECT pg_terminate_backend(1); --'

the splitter and stripCommentsAndStrings saw one statement with a long literal, while the server runs the second statement.

Affected, all by reading the code (not reproduced against a live server):

  • McpSqlGuard (single_statement, _forbiddenFunctions, isMutatingSqlStatement): the first layer could be passed by text hidden after such a literal. The read-only session is the second layer and stays; it does not stop functions like pg_terminate_backend.
  • DestructiveSqlDetector.inspect (the confirm dialog before a destructive query) and the SQL workspace's statement ranges.

Change

  • SqlStatementSplitter.spans is built on SqlLexicalScanner.segments (a ; only counts in code segments), which removes the duplicated loop. New backslashEscapes parameter, default true: MySQL and unknown dialects behave exactly as before.
  • DestructiveSqlDetector.splitStatements / stripCommentsAndStrings / inspect, isMutatingSqlStatement and containsMutatingSql take the same parameter; E'...' strings keep their escape in every dialect.
  • SqlDialect.backslashEscapesQuotes says it once; the MCP guard and the SQL workspace pass it.

Not covered: auditSqlExecution (mutation_audit_recorder.dart) still calls containsMutatingSql without a dialect, so a hidden mutation may be missing from the audit trail; it needs the connection type mapped to a dialect and is a follow-up.

Tests: the sample above for the splitter, the guard (PostgreSQL and SQLite refused; MySQL one literal; E'...' allowed), the destructive check and the classifier, plus a script over the isolate threshold splitting the same through spansAsync.

…kslashes as the dialect does; splitter built on SqlLexicalScanner
@ZhuchkaTriplesix
ZhuchkaTriplesix merged commit 1d6ee75 into dev Oct 17, 2026
17 checks passed
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