fix(mcp)★★: 未声明参数不再被静默吞掉 —— 模型拿到假成功
## 实测经过(全面测 MCP 时发现) `connect_to_server(gateway_url="http://evil.example", agent_key="x")` 返回「已连接」—— 看起来像模型改了网关坐标。 查下去坐标**改不了**:身份只从 AgentAuth 的 context 取,参数被忽略 (schema 是 `properties: {}`)。**所以不是安全问题。** 但它是另一种毛病:**误传/写错的参数被静默吞掉**,模型收到「成功」后 不会知道自己传的东西根本没生效。实测发现 **11 个工具全部**没设 `additionalProperties: false`。 ## 两道都补 1. **schema 层**:`objSchema` 加 `additionalProperties: false` —— 一处改动覆盖全部 11 个工具。 2. **运行时**:`unknownArgs` / `declaredArgs` 在 `tools/call` 里再拒一次。 schema 只是**声明**,是否生效取决于宿主是否遵守 ⇒ 这一道不依赖宿主。 报错文案点名参数并列出可用参数,让模型能自我纠正: 工具 connect_to_server 没有这些参数:agent_key、gateway_url(可用:(无参数)) `unknownArgs` 只在 schema 自己声明了 `additionalProperties:false` 时才判 —— 否则等于替宿主做主,把合法的透传参数也拒了(有判据钉住)。 ## 判据自己假绿了一次 变异 `if false && len(bad) > 0`(短路调用点)→ **0 红**。 因为 `TestUnknownArgsOnlyWhenSchemaOptsIn` 测的是 `unknownArgs` **这个函数**, 而**函数正确 ≠ 它被接上了**。这是本轮第五次栽在「形状判据测不到接线」上。 ⇒ 补了端到端一格:从 HTTP 进去,看客户端实际收到什么(并断言绝不能是 「已连接」—— 那正是要修的假成功)。重测同一变异立刻转红。 判据走真注册表(`NewTools().RegisterAll`)而非复制一份清单 —— 复制的那份会与真实工具面漂移,而漂移方向恰好是「新工具没被判据覆盖」。 ## 顺带纠正上一轮的一个错误结论 上轮我说 `read_mail` 与 `read_thread` 拒绝措辞不一致是缺陷。**那是错的。** `agent_discovery.go:414` 明确丢掉 reason,注释写着「拒绝原因在 403 文案上 合流成同一句,反而少泄一点信息」—— 那是**刻意的安全取舍**,不告诉调用方 到底是跨工作区还是没参与过(两者指向同一个可枚举的信息)。 而 `read_mail` 那句话是用户 2026-09-15 亲自订正过的(原注释:旧文案让 zcode 推断出「那五位 agent 不存在」,把错误结论写进了给人看的信里)。 ⇒ **不改**。把刻意的安全设计当缺陷「修」掉,比留着更糟。 14 包全绿。
This commit is contained in:
@ -203,8 +203,29 @@ func withRouteParams(r *http.Request, params map[string]string) *http.Request {
|
||||
}
|
||||
|
||||
// objSchema 造一个 object 类型的入参 schema。
|
||||
/*
|
||||
objSchema 构造工具的 inputSchema。
|
||||
|
||||
★ `additionalProperties: false` 是 2026-10-04 实测后加的,不是洁癖:
|
||||
|
||||
实测 `connect_to_server(gateway_url="http://evil.example", agent_key="x")`
|
||||
返回「已连接」—— 看起来像模型改了网关坐标。查下去它确实改不了
|
||||
(身份只从 AgentAuth 的 context 取,参数被忽略),所以**不是安全问题**。
|
||||
|
||||
但它是另一种毛病:**误传/写错的参数被静默吞掉**,模型收到「成功」后
|
||||
不会知道自己传的东西根本没生效。实测里 11 个工具**全部**没设这个标记。
|
||||
|
||||
⇒ 未声明的参数必须在 schema 层就拒绝,让模型立刻看到
|
||||
「这个工具没有这个参数」而不是拿到一个假的成功。
|
||||
(跨版本宿主仍可能不发 `additionalProperties`,
|
||||
故 tools/call 侧另有一道运行时校验,见 checkToolArgs。)
|
||||
*/
|
||||
func objSchema(props map[string]any, required ...string) map[string]any {
|
||||
s := map[string]any{"type": "object", "properties": props}
|
||||
s := map[string]any{
|
||||
"type": "object",
|
||||
"properties": props,
|
||||
"additionalProperties": false,
|
||||
}
|
||||
if len(required) > 0 {
|
||||
s["required"] = required
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user