Files
SuperBizAgent-java/mvp/engineering/harness/Harness MySQL 沙箱学习笔记-从 SQL 校验到脱敏投影.md
T
zhuyongxin 074d1aa5a9 docs(harness): add MySQL sandbox learning note and mark tool domain complete
- Add MySQL sandbox note: three defense layers (semantic/connection/output),
  fail-closed validation, allowlist, parameterization, cancellation, redaction
- Mark tool domain complete in roadmap; next: guard
2026-08-06 17:46:53 +08:00

8.3 KiB
Raw Blame History

Harness MySQL 沙箱学习笔记:从 SQL 校验到脱敏投影

更新日期:2026-08-04 主题:query_mysql 工具完整链路——三层防线(语义/连接/输出) 配套:tool 域代码学习笔记(注册/调用/执行全链路)

1. 定位:可查询、不可破坏、不可越界、不可拖库

query_mysql 让模型查询授权数据库,但封死三种攻击面:

破坏:写/删/改(非 SELECT)→ 语义层拒绝
越界:未授权表/列 → 白名单拒绝
拖库:全表通配(*)/无界读取 → 禁通配符 + 三重有界截断

为什么 MySQL 要安全层而 RAG 不要:Milvus 天然只读检索无破坏面;MySQL 直接连数据库,SELECT 之外全是风险面——安全设计随攻击面走。

2. 架构总览(三层防线 + 接线员)

flowchart LR
    E["MysqlToolAdapter<br/>接线员"] -->|"parse 请求"| V["第 1 层 语义层<br/>MysqlSqlValidator<br/>AST fail-closed"]
    V -->|"MysqlQueryPlan"| X["第 2 层 连接层<br/>JdbcMysqlReadOnlyExecutor<br/>JDBC 只读+超时+取消"]
    X -->|"MysqlRawResult<br/>(Harness-only)"| P["第 3 层 输出层<br/>MysqlResultProjector<br/>脱敏+有界"]
    P -->|"MysqlToolResult<br/>(冻结契约)"| B["ToolBoundary<br/>统一门禁"]

接线员:Adapter 把 validator/executor/projector 组装进 ToolBoundary;任何安全/参数异常统一映射 INVALID_REQUEST(不泄露内部细节)。

3. 定义层(6 个小文件)

类 作用
MysqlReadOnlyExecutor 函数式接口(执行端口):plan + RunContext → raw 结果
MysqlQueryPlan 执行计划:request + dataSource + normalizedSql + params(params 深拷贝)
MysqlRawResult Harness-only raw 结果:columns + rows + truncated(不直接给 Agent)
MysqlSecurityException 安全异常:Adapter 映射稳定错误码
MysqlToolLimits 限额:100 行 / 2000 字单元 / 64KB 总字节 / 5 秒超时
MysqlDataSourceDefinition 逻辑数据源 + schema → table → 列三级白名单(访问边界)

白名单深拷贝(TreeSet 保证确定性)+ defaultSchema 必须出现在白名单里——配置即边界。

4. 第 1 层:Validator——语义层(fail-closed)

4.1 禁止清单(JSqlParser 解析 AST,逐节点拒绝)

非 SELECT / 多条语句 / WITH / 子查询(SubSelect) / 通配符投影(* 和 t.*)
窗口函数(Analytic) / CASE / EXISTS / 分层查询(OracleHierarchical)
锁读(FOR UPDATE / SKIP LOCKED) / OFFSET/FETCH/TOP/SKIP/FIRST/OPTIMIZE FOR
字面量:StringValue/LongValue/DoubleValue/HexValue/DateValue/TimeValue/TimestampValue
      —— 全部必须参数化(防注入的最强形态)

4.2 允许清单

白名单内表列的 INNER/LEFT JOIN(禁 CROSS/RIGHT/FULL/OUTER)
聚合函数:COUNT/SUM/AVG/MIN/MAX(COUNT(*) 只允许 COUNT)
占位符参数 ?(PreparedStatement 绑定)

4.3 附加校验

恰好一条语句 + 必须是 Select + 无 WITH + 普通 PlainSelect(禁集合操作/值语句)
FROM 必须是白名单内实体表(禁子查询来源);重复别名拒绝(不区分大小写)
列校验:限定表 → 查该表列白名单;未限定 → 已注册表恰好一个命中(防歧义/未授权)
占位符数量 == params 数量(防参数错位/少传)

4.4 fail-closed 原则

任何解析/校验异常 → 统一转 MysqlSecurityException(默认拒绝,不是默认放行)
业务规则违规原样穿出;解析/未知异常包装统一信息(不泄露内部细节)
→ 安全策略是「拒绝清单外的全允许」的反面:「允许清单外的全拒绝」

5. 第 2 层:Executor——连接层(双保险)

connection.setReadOnly(true)    ← JDBC 连接层强制只读(语义层之外的物理防线)
PreparedStatement 参数化        ← 占位符绑定(防注入第二道)
setQueryTimeout(5s)             ← 慢查询截断
setMaxRows(maxRows+1)           ← 多取一行用于检测截断
context.cancellation().onCancel → statement.cancel()   ← Run 取消联动
checkRun 每行检查               ← 取消/超时即刻中止(与 core 终态联动)
jsonSafe:byte[] → Base64;字符串截断 maxCellChars
estimatedBytes:字节预算超限移除末行并标记截断

关键:查询不是独立资源——受 Run 生命周期管(取消 → 立即 cancel 语句),与 RAG 检索同理(checkRun 与 Harness core 终态联动)。

6. 第 3 层:Projector——输出层

6.1 脱敏(输出时,不是查询时)

列名含 password/passwd/token/secret/api_key/apikey/credential → [REDACTED]
→ raw 保留真实值,只对 Agent 可见层脱敏(查询照常执行,输出才遮)

6.2 有界(三重截断 + 兜底)

行数(maxRows) + 单元格字符(maxCellChars) + 总字节(maxResultBytes)
列名必须非空且唯一(防歧义投影)
fitBudget 兜底:逐行裁掉尾部 → 裁空诚实降级 NO_EVIDENCE → 仍超限 fail closed

6.3 客观证据语义

rows 空 → NO_EVIDENCE;非空 → EVIDENCE_FOUND
→ 与 RAG 的 evidence_status 同一套契约(证据状态由「有没有内容」客观决定)

7. 与 RAG 对照(同类架构,不同复杂度)

query_mysql lookup_knowledge
安全层 有(Validator + 只读连接 + 脱敏) 无(Milvus 天然只读检索)
后端复杂度 简单(Validator→Executor→Projector) 复杂(三段 + 降级 + 双 trace)
raw → 契约 MysqlRawResult → MysqlToolResult LookupResult → RagToolResult
数量限制 maxRows=100 / 64KB returnN=5 / 8 条 / 16KB
证据语义 rows 空不空(NO_EVIDENCE/EVIDENCE_FOUND) evidenceBlocks + relevance_level
与 Harness 衔接 同为 Boundary/Projector/Adapter 模式 同为 Boundary/Projector/Adapter 模式

8. 易错点

易错 正确
Validator 只查 SELECT 还有白名单表列、禁字面量、禁通配符、占位符计数
语义层够了 连接层 setReadOnly + 参数化是物理防线(纵深防御)
查询独立于 Run 查询受 Run 取消/超时联动(onCancel → statement.cancel)
脱敏在查询层 脱敏在投影层(raw 保留真实值,只对 Agent 脱敏)
有界只限行数 行 + 单元格 + 字节三重截断 + fitBudget 兜底
安全异常抛原样 统一映射 INVALID_REQUEST(不泄露内部细节)
字面量可以清洗放行 字面量全拒必须参数化(清洗是弱防线,参数化是强防线)

9. 面试话术(30 秒)

"query_mysql 是三层防线的只读沙箱:语义层(JSqlParser 解析 AST,fail-closed 拒绝一切不安全形态——非 SELECT、多语句、子查询、通配符、字面量、未授权表列、锁读全部拒绝,只允许白名单表列的 INNER/LEFT JOIN 和聚合 + 参数化占位符,且占位符数量必须与 params 匹配);连接层(JDBC setReadOnly + PreparedStatement 参数化 + 超时 + maxRows + 取消联动——Run 取消立即 cancel 语句);输出层(敏感列脱敏 [REDACTED] + 行/单元格/字节三重截断 + rows 空不空定证据状态)。安全异常统一映射稳定错误码,不泄露内部细节。"

10. 代码位置索引

类 文件
MysqlToolAdapter src/main/java/com/superbiz/agent/harness/tool/adapter/MysqlToolAdapter.java
MysqlSqlValidator src/main/java/com/superbiz/agent/harness/tool/mysql/MysqlSqlValidator.java
JdbcMysqlReadOnlyExecutor src/main/java/com/superbiz/agent/harness/tool/mysql/JdbcMysqlReadOnlyExecutor.java
MysqlResultProjector src/main/java/com/superbiz/agent/harness/tool/mysql/MysqlResultProjector.java
MysqlDataSourceDefinition src/main/java/com/superbiz/agent/harness/tool/mysql/MysqlDataSourceDefinition.java
MysqlReadOnlyExecutor src/main/java/com/superbiz/agent/harness/tool/mysql/MysqlReadOnlyExecutor.java
MysqlQueryPlan / MysqlRawResult src/main/java/com/superbiz/agent/harness/tool/mysql/MysqlQueryPlan.java 等
契约(MysqlToolRequest/Result) src/main/java/com/superbiz/agent/harness/tool/contract/MysqlTool*.java