Skip to content

fix(database): 生产保护按词法判定危险 SQL,执行前先由后端确认 - #1469

Closed
1lck wants to merge 1 commit into
previewfrom
fix/database-sql-confirmation-guard
Closed

1lck wants to merge 1 commit into
previewfrom
fix/database-sql-confirmation-guard

Conversation

@1lck

@1lck 1lck commented Oct 24, 2026

Copy link
Copy Markdown
Owner

背景

在开启了"生产保护"的数据库连接上,所有带 WHERE 的 UPDATE/DELETE 都执行失败。报错是 Database request failed (confirmation_required): Production protection requires explicit confirmation,界面上也不会弹出确认框。

根因:

  1. macOS 执行 SQL 前会先格式化,把 WHERE 挪到新的一行(UPDATE items\nSET value = 2\nWHERE id = 1)。
  2. sidecar 判断"危险 SQL"时用的是 upper.contains(" WHERE "),只认前后带空格的 WHERE,换行后的 \nWHERE 匹配不上,于是被当成"没有 WHERE 的 UPDATE"拒绝。
  3. 本地 Swift 分析器认为不需要确认,所以没有弹框,用户看到的只有一条报错。

同一套判定还有漏判,开头加注释(-- x\nDELETE FROM users)、WITH … DELETE、多语句脚本都能绕过。

按数据库插件化提案(#1466),危险 SQL 规则以后只保留在 Rust 后端一份,Swift 那份会删掉。所以这次的修法是让后端判断准确并成为权威,而不是去对齐 Swift 的字符串规则。

改动

1. sidecar:危险 SQL 判定改为词法分析(rust/lithe-db-sidecar/src/main.rs)

  • is_dangerous_sql(kind, sql) 先做词法切分,跳过:
    • -- 和 /* */ 注释(支持 PostgreSQL 的嵌套块注释);
    • 单引号、双引号、反引号;
    • $$ / $tag$ 块($1 占位符不算);
    • SQL Server 的 [标识符]。
  • 反斜杠转义按方言区分:MySQL/MariaDB 的字符串和 PostgreSQL 的 E'' 字符串支持反斜杠转义,其它方言只认双写引号。为此判定需要拿到连接的 kind。
  • 按顶层 ; 拆分语句,逐条检查。
  • 以下情况算危险:
    • 语句以 RESTORE 开头;
    • 出现 DROP / TRUNCATE,包括 ALTER TABLE … DROP COLUMN。但 TRUNCATE( 是 MySQL 的数值函数,不算;
    • UPDATE/DELETE 处于动词位置(语句开头、( 之后,或 WITH … ) 之后),而且同一层括号内没有 WHERE。子查询、注释、字符串里的 WHERE 都不算数;数据修改型 CTE(WITH d AS (DELETE …))也会被识别。
  • 以下用法不再误判:SELECT … FOR UPDATE、ON DELETE CASCADE、ON DUPLICATE KEY UPDATE、GRANT UPDATE, DELETE。
  • ensure_write_allowed / ensure_transaction_allowed 改用新判定,返回的错误码不变。
  • 新增 checkWriteProtection 方法:不连接数据库,只返回 {"requiresConfirmation": bool},用的规则和 execute 相同。

2. macOS:执行前先由后端确认

  • DatabaseOperations / DatabaseSidecarService 新增 requiresWriteConfirmation(connection:sql:),对应调用 checkWriteProtection。
  • DatabaseFeatureModel.sqlConfirmation(forSQLTab:scope:):
    • 本地分析器要求确认时,直接返回本地结果;
    • 否则,如果连接开了生产保护且批次里有写语句,就先问 sidecar。需要确认时返回带提示文案的 analysis;
    • 只有查询语句、或连接没开生产保护时,不调用 helper;
    • helper 调用本身失败时直接放行,由 execute 报出错误,后端仍会拒绝未确认的危险 SQL。
  • DatabaseSQLWorkspaceView.run(scope:) 改为先 await sqlConfirmation:
    • 需要确认就弹出原来的 "Confirm database change" 确认框,确认后带 confirmedRisk: true 执行;
    • 检查期间禁用运行按钮,防止重复触发。
  • 为什么要在执行前问:macOS 是逐条执行批次的。如果等 execute 返回 confirmation_required 再弹框,前面的语句已经生效,确认后整批重跑会把它们再执行一遍。

3. 测试与矩阵

  • Rust:
    • write_protection_classifies_dangerous_sql:危险/安全金样例,覆盖换行和 tab 前的 WHERE、注释、字符串、子查询、CTE、多语句、FOR UPDATE、ON DELETE CASCADE、$$ 块、嵌套注释等;
    • write_protection_lexes_quotes_per_dialect:MySQL 反斜杠转义、PostgreSQL 标准字符串与 E''、SQL Server [where];
    • write_protection_check_matches_execute_guard_without_connecting:checkWriteProtection 的行为。
  • Swift:databaseSQLConfirmationAsksTheHelperBeforeProtectedWrites 覆盖三种情况:
    • 本地分析器漏判(子查询 WHERE)时由 helper 要求确认;
    • 带换行 WHERE 的写入和纯查询都不弹框,查询不会请求 helper;
    • 没开生产保护时不请求 helper。
  • shared/platform-feature-matrix/features/database-query-history.json:macOS 证据增加 FeatureModel、sidecar 和测试文件,验收说明补上生产保护确认行为。

验证

  • cargo test -p lithe-db-sidecar:15 passed。clippy 对改动行无新增警告(其余警告是原有代码)。
  • ./scripts/test-macos.sh --filter 'databaseSQLConfirmation…|databaseSQLBatch…|databaseTreeRejectsLateResults…':通过。
  • 同一过滤器下的完整数据库 Swift 套件,在干净 preview(53f7052a4)上也有 11 组失败,包括 databaseTreeRenders{Dark,Light}Theme、sqlExecutionLock…、delayed… 等,与本 PR 无关。
    • 只在本分支全量运行时出现的 delayedPageOnlyClearsItsOwnCurrentDrafts:单独运行连续 3 次全部通过,在 preview 上单独运行也通过,属于并发下的不稳定,不经过 SQL 执行路径。
    • 只在 preview 全量运行时出现的 draftConnectionTestDoesNotSaveOrNavigate:情况相同。
  • verify-test-stability.sh、verify-service-boundaries.sh、verify-runtime-bundle-immutability.sh(test-macos 内执行)、verify-platform-feature-matrix.sh、verify-platform-feature-matrix-change.sh origin/preview HEAD、generate-platform-feature-matrix.mjs --check:全部通过。

未包含

🤖 Generated with Claude Code

- sidecar 的危险 SQL 判定从字符串前缀/" WHERE " 匹配改为词法分析:去掉注释、
  字符串、引号标识符和 $$ 块;按语句逐条检查;WHERE 必须直接限定该
  UPDATE/DELETE(子查询里的 WHERE 不算);识别 WITH ... DELETE 与数据修改型
  CTE;DROP/TRUNCATE(含 ALTER ... DROP)需要确认;排除 FOR UPDATE、
  ON DELETE CASCADE、GRANT、MySQL TRUNCATE() 等非写入用法;MySQL/MariaDB
  与 PostgreSQL E'' 字符串按反斜杠转义处理,SQL Server 识别 [标识符]。
- 修复根因:执行前格式化会把 WHERE 换到新行,旧判定只认 " WHERE ",导致
  生产保护连接上所有带 WHERE 的 UPDATE/DELETE 都被拒绝。
- sidecar 新增不连库的 checkWriteProtection 方法;macOS 在生产保护连接执行写入
  前先问后端,需要确认时弹出原有确认框,避免批量执行到一半被拒。
- 补充 Rust 金样例测试与 Swift 预检测试。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ghfind-review ghfind-review Bot added the review: high ghfind author score; see https://ghfind.com label Oct 24, 2026
@lithe-code-review

lithe-code-review Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Lithe Review

结论: ❓ 审查未完成

任务: 332

审查状态:已由服务器取消

可以重新评论 @lithe review 召唤。维护者可通过任务编号查看服务器日志。

@1lck

1lck commented Oct 24, 2026

Copy link
Copy Markdown
Owner Author

已合并到 #1470(数据库模块插件化的整体 PR),后续在那里继续推进。

@1lck 1lck closed this Oct 24, 2026
@1lck
1lck deleted the fix/database-sql-confirmation-guard branch October 24, 2026 12:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review: high ghfind author score; see https://ghfind.com

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant