package acp import ( "encoding/json" "testing" ) // TestAllowlistHit 覆盖白名单匹配的边界:空 toolName 一律不放行(含 "*"), // 白名单中的空串条目不误匹配。 func TestAllowlistHit(t *testing.T) { cases := []struct { name string allowlist []string toolName string want bool }{ {"精确命中", []string{"safe.tool"}, "safe.tool", true}, {"未命中", []string{"safe.tool"}, "other.tool", false}, {"通配符放行可识别工具", []string{"*"}, "any.tool", true}, {"通配符不放行空名", []string{"*"}, "", false}, {"空名不匹配空串条目", []string{""}, "", false}, {"空白名单", nil, "any.tool", false}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { if got := allowlistHit(c.allowlist, c.toolName); got != c.want { t.Fatalf("allowlistHit(%v, %q) = %v, want %v", c.allowlist, c.toolName, got, c.want) } }) } } // TestExtractToolName_IgnoresAgentAssertedKindTitle 锁定安全约束:extractToolName // 只信任真实工具名(name / toolName),绝不回退到 agent 自填的 kind / title。否则 // agent 可把 execute/shell 调用伪装成 kind='read' 或冒用白名单 title 绕过人工审批。 func TestExtractToolName_IgnoresAgentAssertedKindTitle(t *testing.T) { cases := []struct { name string toolCall string want string }{ {"真实 name", `{"name":"shell.exec"}`, "shell.exec"}, {"真实 toolName", `{"toolName":"shell.exec"}`, "shell.exec"}, {"仅 kind 不取", `{"kind":"read"}`, ""}, {"仅 title 不取", `{"title":"Read file"}`, ""}, {"kind+title 都不取", `{"kind":"execute","title":"safe.tool"}`, ""}, {"name 优先于 kind/title", `{"name":"real.tool","kind":"read","title":"x"}`, "real.tool"}, {"空 toolCall", ``, ""}, {"无可识别字段", `{"unknownField":1}`, ""}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { got := extractToolName(json.RawMessage(c.toolCall)) if got != c.want { t.Fatalf("extractToolName(%q) = %q, want %q", c.toolCall, got, c.want) } }) } } // TestAllowlist_DoesNotHonorAgentKind 端到端验证 bug #3:即使 allowlist 含 'read' // 这类 kind 值,agent 把 execute 调用标成 kind='read' 也不会自动放行(extractToolName // 返回空 → allowlistHit 为 false)。 func TestAllowlist_DoesNotHonorAgentKind(t *testing.T) { toolName := extractToolName(json.RawMessage(`{"kind":"read","title":"safe"}`)) if toolName != "" { t.Fatalf("kind/title 不应被当作工具名, got %q", toolName) } if allowlistHit([]string{"read", "safe", "*"}, toolName) { t.Fatal("kind/title 伪装的调用不应命中白名单(含通配符)") } }