package permission // 规则 deny 引擎 -- 编译期已知的危险模式匹配. // // 规则引擎是分类器的第二层过滤,负责拦截已知的危险操作. // 它只做 deny,不做 allow -- 这是一个关键的设计决策. // // 升华改进(ELEVATED): 规则引擎只负责 deny,不负责 allow. // 这避免了规则的 false negative(误放行)比 AI 的更危险. // 规则匹配是确定性的,如果规则说"这是危险的",那一定是危险的. // 但如果规则说"这是安全的",可能遗漏了新的攻击模式. // 所以 allow 的决策权交给更灵活的 AI 分类器. // 替代方案:规则既 allow 又 deny(原始设计中没有独立规则引擎, // 所有判断由 AI 完成,白名单只是跳过优化). import ( "strings" ) // RuleDenyEngine 规则 deny 引擎. // 只做一件事:检查工具调用是否匹配已知的危险模式. type RuleDenyEngine struct { rules []DenyRule } // DenyRule 是一条 deny 规则. type DenyRule struct { Pattern string // 匹配模式(在工具输入中搜索) Reason string // 拒绝原因 Category string // 分类(command / file / network) ToolName string // 限定工具名(空表示匹配所有工具) } // BuiltinDenyRules 内置 deny 规则(编译进引擎). // // 历史包袱(LEGACY): 这些规则来源于多年的安全事件积累, // 每一条背后都有一个"差点出事"的故事. // 例如 `:(){:|:&};:` 是 fork bomb,曾经有 Agent 在尝试解释 // shell 语法时意外执行了它. var BuiltinDenyRules = []DenyRule{ // 文件系统破坏 {Pattern: "rm -rf /", Reason: "destructive: removes root filesystem", Category: "command"}, {Pattern: "rm -rf ~", Reason: "destructive: removes home directory", Category: "command"}, {Pattern: "rm -rf /*", Reason: "destructive: removes all root entries", Category: "command"}, {Pattern: "> /dev/sda", Reason: "destructive: writes to raw disk", Category: "command"}, {Pattern: "> /dev/nvme", Reason: "destructive: writes to raw disk", Category: "command"}, // 磁盘格式化 {Pattern: "mkfs.", Reason: "destructive: formats filesystem", Category: "command"}, // 原始磁盘操作 {Pattern: "dd if=", Reason: "potentially destructive: raw disk operation", Category: "command"}, {Pattern: "dd of=/dev/", Reason: "destructive: writes to raw device", Category: "command"}, // fork bomb 和资源耗尽 {Pattern: ":(){ :|:& };:", Reason: "fork bomb", Category: "command"}, {Pattern: ":(){:|:&};:", Reason: "fork bomb (no spaces)", Category: "command"}, // 网络数据外泄 {Pattern: "curl -d @/etc/passwd", Reason: "data exfiltration: sends passwd file", Category: "network"}, {Pattern: "curl -T /etc/shadow", Reason: "data exfiltration: uploads shadow file", Category: "network"}, // 权限提升 {Pattern: "chmod 4755", Reason: "setuid bit: potential privilege escalation", Category: "command"}, {Pattern: "chmod u+s", Reason: "setuid bit: potential privilege escalation", Category: "command"}, // 系统关键文件覆写 {Pattern: "> /etc/passwd", Reason: "destructive: overwrites passwd file", Category: "file"}, {Pattern: "> /etc/shadow", Reason: "destructive: overwrites shadow file", Category: "file"}, // 精妙之处(CLEVER): 隐蔽的代码执行向量. // 以下命令表面上是"文本处理工具",但内置了执行任意 shell 命令的能力. // 和 sed 的 e 标志一样危险,但更容易被忽略. // 注意:这些规则不替代 sed 专项验证(sed_security.go),是独立的补充层. // awk system() - 在 awk 脚本中执行 shell 命令 {Pattern: "system(", Reason: "code execution: awk/perl system() call", Category: "command", ToolName: "Bash"}, // awk 管道输出 - 通过管道执行命令 {Pattern: "| getline", Reason: "code execution: awk pipe to getline", Category: "command", ToolName: "Bash"}, // find -exec / -execdir - 对搜索结果执行命令 {Pattern: "-exec ", Reason: "code execution: find -exec runs commands on matched files", Category: "command", ToolName: "Bash"}, {Pattern: "-execdir ", Reason: "code execution: find -execdir runs commands in file's directory", Category: "command", ToolName: "Bash"}, // xargs - 从标准输入构建命令执行 {Pattern: "| xargs ", Reason: "code execution: xargs builds and executes commands from input", Category: "command", ToolName: "Bash"}, } // NewRuleDenyEngine 创建规则 deny 引擎. // 如果 rules 为 nil,使用 BuiltinDenyRules. func NewRuleDenyEngine(rules []DenyRule) *RuleDenyEngine { if rules == nil { rules = make([]DenyRule, len(BuiltinDenyRules)) copy(rules, BuiltinDenyRules) } return &RuleDenyEngine{rules: rules} } // Check 检查工具调用是否匹配 deny 规则. // 返回 (是否被拒绝, 拒绝原因). func (e *RuleDenyEngine) Check(toolName string, input map[string]any) (bool, string) { if len(e.rules) == 0 { return false, "" } // 将输入参数序列化为可搜索的文本 searchText := buildSearchText(toolName, input) if searchText == "" { return false, "" } searchLower := strings.ToLower(searchText) for _, rule := range e.rules { // 如果规则限定了工具名,检查是否匹配 if rule.ToolName != "" && rule.ToolName != toolName { continue } patternLower := strings.ToLower(rule.Pattern) if strings.Contains(searchLower, patternLower) { return true, rule.Reason + " (pattern: " + rule.Pattern + ")" } } return false, "" } // AddRule 向引擎追加一条 deny 规则. func (e *RuleDenyEngine) AddRule(rule DenyRule) { e.rules = append(e.rules, rule) } // Rules 返回当前的规则列表(只读副本). func (e *RuleDenyEngine) Rules() []DenyRule { out := make([]DenyRule, len(e.rules)) copy(out, e.rules) return out } // buildSearchText 将工具输入构建为可搜索的文本. func buildSearchText(toolName string, input map[string]any) string { if input == nil { return "" } var sb strings.Builder // Bash 工具:搜索 command 字段 if cmd, ok := input["command"].(string); ok { sb.WriteString(cmd) } // 文件工具:搜索 file_path 字段 if fp, ok := input["file_path"].(string); ok { if sb.Len() > 0 { sb.WriteString(" ") } sb.WriteString(fp) } // 通用:搜索 content 字段 if content, ok := input["content"].(string); ok { if sb.Len() > 0 { sb.WriteString(" ") } // 只搜索前 1000 字符,避免大文件内容拖慢匹配 if len(content) > 1000 { sb.WriteString(content[:1000]) } else { sb.WriteString(content) } } // URL if u, ok := input["url"].(string); ok { if sb.Len() > 0 { sb.WriteString(" ") } sb.WriteString(u) } return sb.String() }