// Package safety 提供 Agent CLI 的安全控制层。 // Logan.Li // 设计参考 opsxcli internal/agent/safety,适配 Web 场景:确认动作改为异步回调 // (由 controller 实现 confirmFn:推送 WS confirm 事件并阻塞等待前端回传)。 // 风险评估逻辑(黑名单 + AssessBashRisk)在 tools 包,本包只做"是否需确认"的策略。 package safety import ( "ezops/pkg/agentcli/tools" ) // Mode 安全模式 // Logan.Li type Mode string const ( ModeStrict Mode = "strict" // medium+ 需确认 ModeBalanced Mode = "balanced" // high+ 需确认(默认) ModePermissive Mode = "permissive" // 仅 critical 处理 ) // ConfirmFn 确认回调:由 controller 实现,返回用户是否批准执行。 // Logan.Li // risk 为动态评估的风险等级,args 为工具参数(含具体 command),供前端展示。 type ConfirmFn func(toolName string, args map[string]interface{}, risk tools.RiskLevel) (bool, error) // Controller 安全控制器 // Logan.Li type Controller struct { mode Mode confirmFn ConfirmFn autoApprove bool // 测试用:自动批准(危险,生产禁用) } // NewController 创建安全控制器 // Logan.Li func NewController(mode Mode) *Controller { if mode == "" { mode = ModeBalanced } return &Controller{mode: mode} } // SetConfirmFn 注入确认回调 // Logan.Li func (c *Controller) SetConfirmFn(fn ConfirmFn) { c.confirmFn = fn } // SetAutoApprove 设置自动批准(仅测试) // Logan.Li func (c *Controller) SetAutoApprove(b bool) { c.autoApprove = b } // riskThreshold 各模式下的确认阈值 // Logan.Li func (c *Controller) riskThreshold() tools.RiskLevel { switch c.mode { case ModeStrict: return tools.RiskMedium case ModePermissive: return tools.RiskCritical default: return tools.RiskHigh } } // Check 执行前安全检查,返回是否允许执行。 // Logan.Li // 策略: // 1. 命中危险命令黑名单(Critical)→ 直接拒绝(硬红线,不可确认放行)。 // 2. 按模式阈值,达到等级 → 调 confirmFn 请用户确认。 // 3. AutoApprove → 跳过确认。 func (c *Controller) Check(tool tools.Tool, args map[string]interface{}) (bool, error) { risk := assessToolRisk(tool, args) // 黑名单硬红线:rm -rf /、mkfs 等绝不允许 if risk >= tools.RiskCritical { return false, nil } // 未达阈值或自动批准:直接放行 if c.autoApprove || risk < c.riskThreshold() { return true, nil } // 达到阈值:请求用户确认 if c.confirmFn == nil { // 无确认回调时,保守拒绝(Web 场景应始终注入 confirmFn) return false, nil } return c.confirmFn(tool.Name(), args, risk) } // assessToolRisk 取工具实际风险:优先动态评估(ssh_execute 看命令),否则静态。 // Logan.Li func assessToolRisk(tool tools.Tool, args map[string]interface{}) tools.RiskLevel { if dt, ok := tool.(tools.DynamicRiskTool); ok { return dt.RiskLevelForArgs(args) } return tool.RiskLevel() }