7.6 KiB
7.6 KiB
vps-commander 生产 Bug Hunt 报告(2026-10-02)
- 测试人:安全测试员(独立 bug hunt)
- 测试时间:2026-10-02 13:36–13:50(+08)
- 测试对象:生产 Hub(wjyhk,13:36:06 重启)+ 6 节点 Agent
- 测试方式:只读探测 + 无害命令(echo/true),未写生产文件、未改配置、未重启服务、未碰 commander.db
- 结论:发现 4 个 bug,最高严重度 高。另有若干“未发现问题”的项如实记录。
【标题】生产 Agent 二进制构建自脏工作树,缺失已提交代码
【严重度:高】
【复现步骤】
- 在 wjyhk 执行
go version -m /opt/vps-commander/vps-commander-agent,观察vcs.revision=a5620cb且vcs.modified=true。 - 对比源码:
git show a5620cb:cmd/agent/main.go的 action switch 只有 6 个 case(mcp_list/mcp_test/mcp_call/exec/file_read/file_write),无 session 相关、无read_multiple_files。 - 但生产 Agent 实际能处理
start_process(session 功能可用),说明二进制内容无法从任何 commit 还原——是脏树构建。 - Hub 同样
vcs.modified=true(13:36 重部署的二进制,revision=556870b)。
【证据】
build vcs.revision=a5620cb9c4d3570cf5ce2abe8772ef4e3b91bd76
build vcs.time=2026-10-01T16:45:10Z
build vcs.modified=true
各节点 Agent md5 一致(35e965a9),均为同一脏构建,于 13:22 统一重启部署。
【影响分析】
- 生产二进制不可复现:出问题时无法用
git checkout <rev> && go build还原现场,排障与回滚失去基准。 - 脏构建内容未知:
read_multiple_files在源码192969d中已加入 Agent,但生产 Agent 不支持(见 Bug #3),说明构建时的脏树处于部分完成状态,可能还缺失其他已提交的安全修复,需逐项核对。 - 建议:从干净 HEAD 重新构建 Hub+Agent,全量重新部署,部署后校验
vcs.modified为空且 revision==HEAD。
【标题】read_multiple_files 在所有远端节点上失败(Hub-Agent 协议不一致)
【严重度:中】
【复现步骤】
vps-mcp.py call read_multiple_files '{"device":"hwsg","paths":["/etc/hostname"],"limit":100}'→ 返回{"error": "unsupported action"}。- 同样调用
device=wjyhk(本地)→ 正常返回文件内容。 - 查源码:
internal/api/mcp_adapter.go:287的ReadMultipleFiles对远端走Cluster.ReadMultipleFiles;Agent 侧cmd/agent/main.go:261在当前源码中有该 case,但生产 Agent 二进制(脏构建)不支持,落入default: unsupported action(main.go:391)。
【证据】
{"error": "unsupported action"}
(hwsg/N3450 等远端节点复现,wjyhk 本地正常)
【影响分析】
- MCP 工具列表向 AI 客户端 quảng cáo
read_multiple_files可用于所有设备,实际 5/6 节点不可用,功能性 bug。 - 根因是 Bug #1(脏构建)的直接后果;修复 Bug #1(干净重建+重部署)后本项自动解决。
- 在修复前,建议 Hub 对不支持该 action 的 Agent 返回明确错误而非透传
unsupported action,或在 capability 协商中隐藏该工具。
【标题】/api/v1/process/session 请求结构体无 JSON tag,snake_case 字段被静默丢弃
【严重度:中】
【复现步骤】
POST /api/v1/process/session?action=start,body{"device":"hwsg","command":"echo hi","session_id":"my-id"}(snake_case,如响应格式所示)。- 响应返回
{"session_id":"04d39d85b150..."}——客户端传的 ID 被丢弃,服务端生成了随机 ID。 - 随后用
my-id做 read/stop →session not found。 - 改用精确大小写
{"SessionID":"my-id"}→ 全流程正常。
【证据】
// internal/api/api.go:784
var req struct {
Device, SessionID, Command, Workdir, Data string // 无 json tag!
Force bool
RingCapacity int `json:"ring_capacity"`
}
Go encoding/json 对无 tag 字段仅做大小写不敏感匹配,session_id(snake_case)无法匹配 SessionID,被静默忽略。
【影响分析】
- API 契约破裂:响应用
session_id(snake_case),请求却要求SessionID(CamelCase);任何按常规 REST 惯例(或照着响应字段)写的客户端都会踩坑,且失败是静默的(拿到一个“成功”但对不上的 session)。 - 非安全漏洞,但属于生产功能 bug。修复:在结构体上加
json:"device"等 snake_case tag(注意与 openapi.json 文档对齐)。
【标题】/api/v1/process/session 不支持 Hub 本节点(无本地回退)
【严重度:中低】
【复现步骤】
POST /api/v1/process/session?action=start,body{"Device":"wjyhk","Command":"echo hi"}。- 返回
device offline(HTTP 409)。 - 对比:
POST /api/v1/exec同样device=wjyhk可正常执行(exec有isRemote本地回退,api.go:744)。
【证据】
// processSession 直接走 Cluster,无 isRemote 判断:
if s.Cluster == nil { http.Error(w, "cluster unavailable", 503); return }
...
result, err = s.Cluster.StartProcess(ctx, req.Device, ...) // wjyhk 无 agent WS 连接 → device offline
【影响分析】
- 功能不一致:exec/read/write 等在 Hub 本节点可用,唯独 session 管理不可用。用户在 Hub 本机无法使用长会话功能。
- 修复:给 processSession 加与 exec 相同的
isRemote本地回退(Hub 侧需持有一个本地 SessionManager,或复用 executor 包)。
未发现问题的项(逐项如实记录)
- 认证绕过:无 token/错 token/空 Bearer 访问
/api/v1/*与/panel/api/*均返回 401;/healthz200 为预期设计。未发现绕过。 - 畸形输入:malformed JSON、空 body、300KB 超大 body(超 256KB MaxBytesReader 限制)均返回 400,无 panic。Hub 日志自 13:24 以来 0 panic、0 error。
- 路径穿越:
../../../etc/hostname可读、\0被validPath拦截(400)。但本工具是已认证管理员工具,无沙箱设计约束,..解析属操作系统正常行为,不视为漏洞。 - 设备名注入:空字符串回退本机(设计如此)、不存在设备/超长字符串/含
\0的设备名均security mode unknown denies exec,fail-closed,无 panic。 - Session 生命周期:Agent 侧每分钟
sessions.Cleanup(),idle TTL 10min + hard TTL 存在;SessionManager有互斥锁,5 并发创建 session 无竞态崩溃(2 成功、3 因达到上限 4 被正确拒绝,上限由MaxSessionBufferTotal/DefaultRingCapacity=4决定,符合设计)。 - 并发压力:10 并行原生 HTTP 请求全部 200、响应快;Hub 内存稳定(~20MB)。30 并行 MCP 调用大量超时系客户端 SSE 建连开销,非 Hub 问题。
- 配额:
ExecLimiter32 并发信号量存在;无单客户端速率限制(设计选择,非 bug)。 - Agent-MCP 畸形参数:不存在的 tool/含
\0的参数返回干净的MCP HTTP status 400,无 panic、无信息泄漏。 - kill 进程:API 只接受 SessionID,Agent 用内部
cmd.Process句柄 kill,不接受客户端 PID,无 PID 重用误杀风险。
附:测试期间观察到的部署活动
- 13:36:06 Hub 被重部署(此前为 13:24),二进制 revision 由
38382d2变为 556870b。测试期间生产处于活跃变更中,部分结论(如 Hub revision)以测试时刻为准。 - Agent 在各节点为同一脏构建(md5 35e965a9,13:22 部署),
vcs.modified=true。
安全测试员,2026-10-02