Files
vps-commander/docs/REVIEW_B_CODE_AUDIT_20261002.md
T
2026-10-02 13:53:00 +08:00

5.7 KiB
Raw Blame History

审核专家 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:687 sessionOwner(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:279 Audit()
  • 事实:无 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