Files
agentic-coding-workflow/internal/acp/permission_service_internal_test.go
2026-06-20 19:16:44 +08:00

73 lines
2.6 KiB
Go

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 伪装的调用不应命中白名单(含通配符)")
}
}