Skip to content

feat(safety): protect non-SQL production writes#3272

Draft
wuxiemian wants to merge 2 commits into
t8y2:mainfrom
wuxiemian:feat/non-sql-production-safeguards
Draft

feat(safety): protect non-SQL production writes#3272
wuxiemian wants to merge 2 commits into
t8y2:mainfrom
wuxiemian:feat/non-sql-production-safeguards

Conversation

@wuxiemian

Copy link
Copy Markdown
Contributor

变更说明

完善生产环境保护,将原本主要覆盖 SQL 写操作的保护机制扩展至非 SQL 连接类型。

主要改动:

  • Redis、MongoDB 支持数据库级生产环境保护。
  • Elasticsearch、Qdrant、Milvus、Weaviate、ChromaDB、Etcd、ZooKeeper、Nacos、MQ、Neo4j、InfluxDB 使用连接级保护。
  • 非 SQL 人工写操作执行前需要二次确认,后端使用短时、单次消费且绑定连接和数据库的写许可。
  • Tauri、Web 和 dbx-core 后端均增加生产写校验,避免仅依赖前端拦截。
  • Redis 未知命令及模块命令默认按写操作处理。
  • MongoDB 聚合 $out$merge 按实际目标数据库进行判断。
  • Redis MOVECOPY ... DBSWAPDBFLUSHALL 按所有受影响 logical DB 判断。
  • AI Agent 和 MCP 始终禁止执行生产环境写操作,不允许获取人工确认许可。
  • AI Agent/MCP 支持跨库生产保护,例如在 B 库执行针对生产库 A 的 SQL、MongoDB 或 Redis 写操作时仍会拦截。
  • 增加非 SQL 写入口静态覆盖测试,防止后续新增写 API 时遗漏生产保护。

本次改动范围较大,涉及前端、Tauri、Web、dbx-core、Node core 和 MCP,需要在不同连接类型及运行模式下反复测试。

当前本地只有 Redis 环境,因此人工联调仅覆盖了 Redis 相关操作。MongoDB、Elasticsearch、向量数据库、Etcd、ZooKeeper、Nacos 和 MQ 目前主要通过单元测试、静态入口覆盖测试及编译检查验证,建议在具备相应服务的环境中继续进行真实连接测试。

变更类型

  • 新功能
  • Bug 修复
  • 性能优化
  • 代码重构
  • 文档更新
  • CI / 构建

涉及前端

  • 本 PR 涉及前端改动,已附截图/录屏(见下方)
PixPin_2026-07-13_11-58-35

验证

  • make check 通过
  • make cargo-check-fast 通过
  • 相关测试通过

已完成:

  • cargo check --no-default-features
  • cargo fmt --all -- --check
  • pnpm typecheck
  • Node core:105 项测试通过
  • MCP 生产保护专项:40 项测试通过
  • 前端生产保护及入口覆盖专项:40 项测试通过
  • 前端全量测试:2838 项测试通过
  • git diff --check
  • Redis 相关生产保护操作人工测试

已知环境限制:

  • MCP 全量测试中的 Windows symlink 用例因系统权限返回 EPERM,业务测试均通过。
  • 非 Redis 连接类型尚未进行真实服务人工联调,需要后续反复测试确认。

关联 Issue

@t8y2 t8y2 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm requesting changes because the production write permit is not bound to the mutation the user confirmed.

The permit is currently keyed only by connection_id + database. The client authorizes first and sends the mutation as a separate request, so another concurrent write on the same connection and database can consume that permit before the confirmed operation arrives. The backend then has no way to prove which mutation was authorized.

Please issue a random, single-use token from the authorization request and require the corresponding mutation to carry it. Consumption should atomically validate the token, connection, effective database, operation kind, and preferably a stable request or target digest, with a short TTL. Adding only the action to the existing shared key is not sufficient because concurrent operations of the same kind can still consume each other's authorization.

@wuxiemian
wuxiemian force-pushed the feat/non-sql-production-safeguards branch from 648aa4b to eac4ced Compare July 13, 2026 12:40
@wuxiemian

Copy link
Copy Markdown
Contributor Author

I'm requesting changes because the production write permit is not bound to the mutation the user confirmed.

The permit is currently keyed only by connection_id + database. The client authorizes first and sends the mutation as a separate request, so another concurrent write on the same connection and database can consume that permit before the confirmed operation arrives. The backend then has no way to prove which mutation was authorized.

Please issue a random, single-use token from the authorization request and require the corresponding mutation to carry it. Consumption should atomically validate the token, connection, effective database, operation kind, and preferably a stable request or target digest, with a short TTL. Adding only the action to the existing shared key is not sufficient because concurrent operations of the same kind can still consume each other's authorization.

已在 eac4ced1 中按建议完成修改。

现在生产写授权不再使用 connection_id + database 的共享许可,而是:

  • 每次确认后签发随机 UUID v4 单次令牌,有效期为 30 秒。
  • 令牌绑定连接、规范化后的有效数据库、操作类型及请求摘要。
  • 请求摘要使用具体 mutation 参数生成稳定的 SHA-256。
  • mutation 通过 Web 请求头或 Tauri 参数携带对应的 token、operation 和 digest。
  • 服务端在同一个 Mutex 临界区内完成校验和删除,确保令牌只能被成功消费一次;不匹配的请求不会消耗令牌。
  • Redis/MongoDB 的跨库操作会先解析实际影响的生产数据库,再进行许可校验。

同时补充了随机令牌、请求不匹配、并发单次消费、同类型并发隔离、过期拒绝以及 Web 授权头完整性测试。这样同一连接、数据库甚至同一操作类型的并发写入也无法再误用其他 mutation 的确认许可。

@t8y2 t8y2 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm requesting changes because the new token fixes the concurrent permit-consumption race, but the request digest is still not verified against the mutation that the backend actually executes.

authorize_production_write stores the digest supplied by the client, and permit consumption only compares it with the digest carried alongside the token. The Redis/Mongo/etc. mutation handlers do not recompute that digest from their deserialized request. A caller can therefore confirm mutation A and submit a different mutation B of the same operation while carrying A's token and digest; the backend will accept and execute B.

Please derive a canonical digest or target descriptor from the actual mutation request on the backend and include it in the atomic permit check. The authorization path should derive the same value from structured intent rather than accepting an arbitrary hash string.

@wuxiemian
wuxiemian marked this pull request as draft July 14, 2026 08:49
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.

2 participants