5.7 KiB
5.7 KiB
审核专家 B:代码实现审计报告
文档属性:历史独立代码审计快照。不要将当时的 BLOCKER 列表直接视为当前未修复列表。
- 审核人:审核专家 B(外部 Agent)
- 日期:2026-10-02
- 对象:
/root/.openclaw/workspace/vps-commander全量 Go 实现 - 对照文档:《VPS-Commander 平滑升级改造与三级安全拦截实施方案》第 7 章
- 方法:逐行审读安全、协议、执行器、API 四大核心包,非风格审查
- 结论:有条件不通过 —— 5 个阻断项未修复前不得进入 Phase 1
一、阻断项(Blockers)
B1. MCP 接口完全绕过三级安全门禁【严重】
- 位置:
internal/api/mcp_adapter.go、internal/mcp/mcp.go - 事实:两文件零处引用安全策略。
MCPBackend.ExecCommand/ReadFile/WriteFile/EditBlock/ListAgentMCP/CallAgentMCP直接执行,不调用requireSecurity。 - 影响:
/mcp/sse是 AI Agent 实际使用的主接口——安全模型只管住了 REST, 管 Agent 的通道完全敞开。审计过程中以普通 MCP 客户端身份调用exec_command未触发任何策略检查。 - 判定:阻断。修复前不得向任何不可信调用方暴露 token。
B2. 单一全局 token,可自行提权【严重】
- 位置:
internal/api/api.go→UpdateSecuritySettingsJSON(/api/v1/security/settings/update) - 事实:仅凭全局 API token 即可修改全局及任意设备的风险等级。实现中无 admin/user 角色区分, Spec 7.2 假设的 "Admin Web" 不存在。
- 影响:拿到 token 即拿到 admin,可把任意设备设为 High,三级门禁形同虚设。
- 判定:阻断。策略变更必须独立鉴权(独立 admin 凭证或 mTLS 管理身份)。
B3. 设备身份自报,per-device 策略不可执行【严重】
- 位置:
internal/api/api.go:467(device := r.URL.Query().Get("device"))、internal/cluster/manager.go:108(device_name取自 query) - 事实:设备名来自请求参数自报;Agent WS 仅以共享 cluster secret 认证,device_name 自报。
- 影响:任意 token 持有者可冒充任意设备,挑选策略最松的设备执行。
- 判定:阻断。需 per-device 凭证(mTLS 或 per-agent token)后 per-device 策略才有意义。
B4. Session 所有权形同虚设【严重】
- 位置:
internal/api/api.go:687sessionOwner(r) - 事实:owner = 全局 token 的 SHA256。单 token 体系下所有调用者 owner 哈希相同,
Session.Authorized()对任何已认证调用者恒为真。 - 影响:Spec 7.5.2 的所有权模型空转,任意调用者可读写/注入/终止他人 Session。
- 判定:阻断。与 B2 同源,需先解决调用者身份体系。
B5. 未知设备 fail-open【严重】
- 位置:
internal/security/policy.go→EffectiveRisk() - 事实:设备不在策略表中 → 回退
GlobalRisk。若 GlobalRisk 为 High,拼错设备名反而获得最高权限。 - 违反:Spec 7.2.3 明确要求 fail-closed(不得默认放行 High)。
- 判定:阻断。未知设备应默认拒绝 Medium/High,或拒绝一切非常读操作。
二、高优先级(Phase 3 前修复)
H1. Session 清理逻辑错误
- 位置:
internal/executor/session_manager.go→Cleanup() - 事实:以
StartedAt计算所谓“空闲超时”(实为最大生命期);已过期但仍在运行的 session 永不清理(只删已结束的)→ 资源泄漏。 - 违反:Spec 7.5.4 要求的空闲超时 + 最大生命期双轨制。
H2. 无 agent 全局 Session 上限与进程创建速率限制
- 位置:
internal/executor/session_manager.go - 事实:
MaxSessionsPerManager = 16仅为单 manager 上限;Start()存在 TOCTOU(解锁→启动进程→加锁复查),并发可短暂超限。 - 违反:Spec 7.6。
H3. 审计日志缺失关键字段
- 位置:
internal/storage/storage.go:279Audit() - 事实:无
actor、无policy_version、无session_id;命令明文入库,参数中的 secret 直接进 DB。 - 违反:Spec 7.7。另需敏感参数脱敏。
H4. Token 允许经 URL query 传递
- 位置:
internal/auth/auth.go→Middleware.Handler - 事实:接受
?token=/?key=/?api_key=。Token 将进入 access log、代理日志。 - 建议:仅允许 Authorization header。
H5. Policy version 无单调性校验
- 位置:
internal/security/policy.go→Publish() - 事实:接受任意版本(含回退)。
- 违反:Spec 7.2.2。
三、值得肯定的实现
- Capability 握手已实现:
Message含ProtocolVersion/AgentVersion/Capabilities,manager.go:333按能力门控——上一轮 spec 评审的阻断项在代码中已解决。 - Session ID 使用
crypto/rand(api.go:698),不可预测。 edit_block使用临时文件 + 原子 rename(edit.go:41-62),含目录 fsync。- REST 路径
requireSecurity覆盖完整:exec/read/write/mcp/edit_block 均接入。
四、总体评价
实现层面的信任模型实质是“持有 token 即上帝”:单 token 无角色分离(B2)、 设备身份自报(B3)、MCP 主通道零门禁(B1)、Session 所有权空转(B4)。 B1–B4 任一单独成立即足以让三级门禁失效,四者并存。
在 B1(MCP 加门禁)、B2(策略变更独立鉴权)、B3(设备身份认证)修复并复审前,
不得向不可信调用方暴露 token,不得开放 start_process 等新能力给 Agent。
审核专家 B,2026-10-02