OpenClaw 微信安全下载体系:确定性 Gate 与 qB 隔离
#OpenClaw #qBittorrent #最小权限 #网络隔离
记录 OC-23:让微信里的“帮我下载 X”能够进入自动化流程,同时不把网页文本、模型判断或一个 magnet 直接等同于下载权限。
相关笔记:《OpenClaw 本地记忆落地与无审批能力边界》 ·
《OpenClaw Exec 审批与生命周期治理》 ·
《OpenClaw 后续功能规划》 ·
《N100 安全事件响应与 fail2ban 加固》
一、问题不在 qB API,而在权限兑换
qBittorrent 的新增、暂停、恢复和删除接口并不复杂。真正危险的是这条隐含链路:
1 | 网页内容 → 模型相信它 → 直接调用下载接口 |
搜索结果和网页正文都可能包含 prompt injection,也可能指向内网、重定向到不同主机,或提供与页面描述不一致的 torrent。仅靠 system prompt 要求模型“注意安全”,不能形成可审计的权限边界。
因此这轮把流程拆成三个角色:
1 | download-discovery → download coordinator → deterministic Gate → qB executor |
-
discovery 只有
web_search和web_fetch,没有 shell 或 qB 权限; -
coordinator 接收微信意图和结构化结果,但不能直接浏览网页或执行 shell;
-
Gate 检查 URL、torrent、infohash 和文件清单,再签发短期单次票据;
-
executor 只接受有效票据,并且只连接 qB 管理网络。
网页负责提供线索,模型负责组织意图,确定性代码才负责兑换执行权限。
二、票据与来源验证
Gate 对 torrent 使用严格 bencode 解析并复算 infohash,同时检查:
-
URL 凭据、协议、每一跳重定向和 DNS 结果;
-
私网、CGNAT、IPv6 ULA 等不可访问目标;
-
tracker 数量、文件数量、目录深度和名称长度;
-
路径穿越、控制字符、Unicode 规范化异常;
-
ISO、可执行文件、压缩包等高风险扩展名。
票据绑定用户身份、微信会话、动作、内容摘要和短有效期。executor 维护一次性消费记录,因此篡改、过期、重放和跨会话使用都会被拒绝。签名材料只存在于受限 secret 中,不进入模型上下文、审计或本文。
对于高风险内容,系统不会因为来源看起来“官方”就自动恢复。例如 Ubuntu 官方 ISO
可以通过来源和元数据检查,但仍要求用户对具体文件作确认。
三、网络和文件隔离
Gate 与 executor 不共享相同网络能力:
| 组件 | 公网 | qB 管理网 | 家庭 LAN |
|---|---|---|---|
| discovery / Gate | 是 | 否 | SSRF 规则拒绝 |
| executor | 否 | 是 | 否 |
| qBittorrent | tracker/HTTP | 本机 WebUI 保留 | 防火墙拒绝 |
新下载先进入 N100 的隔离目录。该目录以 nosuid,nodev,noexec 挂载,即使内容本身恶意,也不能直接借下载目录获得执行语义。qB 到家庭 LAN 的出站被受管规则阻断,公网 tracker、
DNS 和普通 HTTP 保持可用,WebUI 仍可从既有管理入口访问。
四、赛马、删除与急停
多个候选可以创建一个 race 组,但自动裁剪仅作用于本系统创建的组,并有 15 分钟保护窗口。普通“删除记录”和“删除文件”没有被混入免审批入口,仍保留确认。
独立 kill switch 只停止本系统类别的活动任务,不删除数据。审计写入失败时 executor
直接拒绝动作,而不是在没有证据的情况下继续。
五、验收结果
-
12 项恶意 fixture 全部通过;
-
篡改、过期、重放、跨会话和 metadata re-gate 被正确处理;
-
新增、查询、暂停、恢复、赛马、裁剪和测试清理完成;
-
qB 访问家庭路由器被阻断,公网访问与 WebUI 正常;
-
健康快照同时报告实际
ip_forward=1; -
forwarder 为受管用户服务,
Linger=yes; -
测试清理后,qB 的 17 个原有任务与变更前完全一致。
OpenClaw 仍为 2026.6.10,生产 bundle、全局超时及 qB/OpenClaw 账号密码均未修改。
六、仍然保留的边界
这套系统解决的是执行安全和权限最小化,不是全球版权数据库。它无法凭网页文本证明任意影视来源的授权状态;遇到无法形成机器可验证来源的内容时,应向用户索取明确来源或停在发现阶段。
微信里的通用交互审批仍是独立的 OC-22。D2b 只在精确版本源码上制作了 ACP 会话断开时终止等待审批的窄修复;隔离聚焦测试 13/13 通过。它没有修改生产 bundle,也没有用放宽 shell 或增大全局超时绕过生命周期缺陷。
七、微信保留 main,下载按任务分流
最初把微信账号整体绑定到 download coordinator 虽然简单,却会让日常对话、邮件和运维请求也落入下载 agent。最终改成:
1 | 微信 → main |
分流在模型运行前由启动时加载的插件完成,只识别新增、暂停、恢复、进度和赛马等窄意图。下载会话由可信的微信账号与发送者身份派生,HTTP 请求不能通过自带字段冒充微信用户。
main 里的旧 qB skill 已退出自动发现,避免模型再次尝试读取 qB 凭据或通过 shell 直连 API。
发现层也不再把通用 sessions_spawn 暴露给 coordinator,而由单用途
download_discover 内嵌启动隔离 agent。模型输出即使带 Markdown fence、冗余字段或同时给出 torrent 与 magnet,也会先经过固定 schema、长度、候选数和来源字段归一化;网页原文不会直接进入有 qB 权限的上下文。
这次真实冒烟还暴露了 llama.cpp 的 grammar 上限:工具 schema 中 8K/16K 字符重复范围会被当前版本以 “number of repetitions exceeds sane defaults” 拒绝。修复方式是让模型侧 schema
只描述字符串,实际长度与内容边界继续由 Gate/Executor 强制执行,没有放宽执行策略。
最终验证包括:
-
main 到持久 download 会话的查询、暂停、恢复均无需 exec;
-
Ubuntu 官方 torrent 可完成 discovery → Gate,
.iso因高风险扩展名正确停在逐次确认; -
合成
.mkv的添加、查询、暂停、赛马、篡改、重放与跨会话隔离通过; -
插件在网关启动清单中可见,测试会话与任务清理后 qB 回到原有 17 个任务;
-
exec 审批文件未变,日志不再出现读取 qB 密码的命令。
真实微信冒烟随后补出了一个渠道上下文差异:before_dispatch 能识别下载意图,但该版本没有在钩子上下文中完整提供 sender。首版因此 fail-closed,框架回退 main 并生成了一次未执行的 qB curl 审批。修复后,钩子只允许从当前 main session 中 provider 明确为
openclaw-weixin 的持久 origin 补齐身份,与 HTTP handoff 使用相同信任边界;请求正文仍不能提交或覆盖账号、发送者和会话身份。
另一个兼容性问题来自历史任务:原 /query 只看 OC-23 ownership DB,因此改造前或 WebUI
手动添加的 qB 任务会被误报为“没有下载”。最终仅给当前所有者微信身份增加了哈希匹配的全局只读 inventory;其他身份仍只能读取各自会话任务,暂停、恢复和删除也没有随可见范围一起扩大。真实复测可读取全部 17 个既有任务,非所有者合成查询仍为空。
仍有一项后续能力:OC-24 将补齐“最高综合画质”候选排序,以及下载完成后的实际视频规格、默认音轨和中文字幕验收。它不会改变本篇建立的 Gate、会话隔离和普通删除确认边界。
八、从正则分流升级为本地语义分类
确定性正则适合识别“下载进度”“暂停全部下载”等明确控制语句,但面对“刚发你的链接开跑”
“前一个源死了,找个活的替换”等省略表达会持续漏判。继续堆关键词只能扩大维护成本,还会把“如何下载文件”“不要下载,只比较版本”等讨论性请求误送进下载 agent。
这轮用两批完全合成的日常表达比较了多种本地方案:
| 候选 | 准确率 | 下载召回 | 其他召回 | 结论 |
|---|---|---|---|---|
| 0.6B / 1.5B 生成式小模型 | 46.7%–76.7% | 6.7%–80.0% | 73.3%–93.3% | 否决 |
| 多语言 NLI / MiniLM | 56.7%–76.7% | 66.7%–86.7% | 46.7%–66.7% | 否决 |
| 本地 Qwen3.6 35B | 96.7% | 93.3% | 100% | 上线 |
35B 的热态短提示并不携带 OpenClaw 的完整系统上下文,未见 holdout 的 P50/P95 仅为
328/413 ms。容器内真实调用多数为 0.34–0.51 秒;一次与其他推理排队时达到 2.08 秒,因此生产超时设为 3 秒。
分类器只接收当前微信消息,使用既有局域网 llama-server,不调用工具、不浏览网页、不读取
secret,只能返回固定的 download|other JSON。只有 download 且置信度至少 0.8 才会分流;低置信、无效 JSON、超时或桌面模型停机都留在 main。提示注入、明确否定、翻译和知识问答均在测试中保持为 other。
“查看下载进度”仍保留确定性快速通道,避免显卡被桌面应用占用时失去最常用的只读能力。这次升级只改变会话分流,不改变 Gate、票据、qB executor、普通删除确认、网络隔离或审批策略。
九、EVA 日常短句端到端收口
最终以真实日常表达“帮我下载 EVA 旧版 TV 版”重新验收。第一轮暴露的并不是权限缺口,而是发现层的可用性和生命周期问题:搜索 provider 遇到 challenge、网页抓取超时、模型反复创建新的 discovery 会话,以及详情页被误当作 .torrent 解析。
对应修复保持在窄边界内:
-
搜索切换到既有受控 provider,每个隔离 discovery 最多两次搜索、两次抓取;
-
download 外层每个运行只允许一次 discovery,预算耗尽后必须使用已有结果或明确失败;
-
固定 JSON 解析兼容 Markdown fence 和控制字符,但候选数量、字段和长度仍由代码检查;
-
只有精确匹配 Nyaa host 与
/view/<数字>的详情页才转换为站点的/download/<数字>.torrent,随后仍走 SSRF、bencode、infohash 和文件策略检查; -
handoff 错误被插件声明为已处理,main 同时硬拒绝 qB 相关
exec与通用子代理,因而锁冲突或模型失败也不会退回读取凭据、shell 或直连 qB。
最终请求在约一分钟内完成发现、候选检查、Gate 签票和 qB 新增,选中 1080p BluRay、
HEVC 10-bit 的 26 集版本。随后“查看下载进度”和“暂停”均沿同一个微信派生 download
会话执行,qB 实际状态为 stoppedDL、速度为 0,待审批队列为空。
这次还补了 qBittorrent 5.2 的兼容细节:新增任务需要 stopped=true,旧
paused=true 已不足以保证真实暂停。executor 现在同时发送新旧参数以兼容版本差异;没有升级 qB、OpenClaw,也没有修改账号密码、生产 bundle、全局超时、普通删除审批或
allowlist。
上述实现阶段当时完成,但后续真实日志又出现 discovery context overflow、llama.cpp
grammar 初始化失败、download session write lock 超时、可信 handoff 失败,以及删除确认状态回到 main 的回归。因此 OC-23 已重新打开,不能把“组件测试通过”等同于生产全生命周期稳定。
下一次只在 desktop llama-server 可用的窗口,用微信真实短句重新覆盖:
main 分流、隔离发现、多候选质量报告和赛马、Gate 签票、新增、进度、暂停、恢复、无源项清理,以及绑定确认删除。全程不得回退到通用 exec、shell、凭据读取或无限重试;出现错误必须给出明确原因且不留下孤儿任务。GPU 被游戏占用、11435 由用户手动关闭期间暂停这项模型型回归,不把计划停机误报为下载链路故障。
下载完成后的真实名称/规格刷新、最高综合画质排序、默认音轨和中文字幕检查仍归入
OC-24,而不是扩大当前下载执行权限。
