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

107 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 审核专家 B:代码实现审计报告
- **审核人**:审核专家 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*