在下载器里安全地跑第三方 JS:QuickJS 沙箱、SSRF 守卫、进程牢笼的工程实录(Rust + rquickjs)
开源下载管理器 FluxDown 插件系统的安全设计复盘。项目地址:https://github.com/zerx-lab/FluxDown ,官网:https://fluxdown.zerx.dev (AGPL,免费无广告)。利益相关:作者本人。
需求:让下载器「什么都能下」
下载器的死穴不是速度,是「链接它不认识」。与其作者逐站适配,不如学 yt-dlp 把站点适配交给社区:插件 = manifest.json + 一个 JS 文件,核心是 resolve(ctx) 函数——任务启动前把 URL 交给插件换取真实直链,每次启动/续传重解析,直链过期问题天然消失。
但「在宿主进程里执行第三方 JS」是个安全雷区。这篇文章记录我踩过的设计决策。
1. 运行时选型:为什么是 QuickJS(rquickjs)而不是 V8/deno_core
- 引擎是纯 Rust crate,要嵌到桌面 App、headless server、CLI 三个宿主里,V8 的体积与构建复杂度不可接受;
- QuickJS 有原生的 memory limit 与 interrupt handler,资源熔断是引擎级能力;
- 通过 trait 隔离运行时抽象,跨 JS 边界的结构体禁止出现 rquickjs 类型——未来可无痛换 deno_core。
一个坑:rquickjs 叠加 rust-alloc feature 会让 set_memory_limit 静默失效,排查了很久。
2. 三重资源兜底 + 熔断
每次调用新建 AsyncRuntime + AsyncContext(无状态、无跨调用污染),同时挂三道保险:内存上限(QuickJS memory_limit)、CPU 中断(interrupt handler 按 ns 预算切)、墙钟超时(宿主侧 tokio timeout 外层兜底)。连续 3 次 Timeout/MemoryLimit 触发熔断,插件自动禁用并标记原因,用户可见。
3. 网络出口:SSRF 守卫的三条腿
插件能发 HTTP 请求,就必须防它探内网。单一判定函数 is_globally_routable_unicast 复用到三个位置:
- 字面量 IP 前置校验:URL 里直接写内网 IP 直接拒;
- 自定义 DNS Resolver:域名解析结果逐个校验,防 DNS rebinding;
- redirect Policy::custom:重定向逐跳复查,防「先跳外网再 302 回内网」。
4. 能力面:权限声明 + 不注入即不存在
插件想调 ffmpeg / yt-dlp,必须在 manifest 写 permissions。没声明的插件,flux.ffmpeg 这个对象根本不会注入到 JS 全局——不是运行时报错,是 undefined。安装时权限清单对用户可见。
5. 子进程牢笼:near-raw argv + 参数校验器
ffmpeg/yt-dlp 走「近乎全量 CLI 透传」而不是逐能力封装 API,但 argv 逐 token 过校验器:
- ffmpeg:拒 URL scheme、协议前缀(file:/concat:)、绝对路径/盘符/…,文件引用一律相对 cwd=下载目录牢笼;除法(30000/1001)、流选择器(0:a)、滤镜(scale=1280:720)等合法语法放行;
- yt-dlp:本职是抓站所以放行 URL,但封 13 个危险开关(–exec、–config-location、–cookies-from-browser 等),自动前置 --ignore-config,cwd 是插件独立 scratch 目录;
- 两者都是 off-actor 子进程 + kill_on_drop + 全局并发 2 + 超时上限。
托管的 ffmpeg 不在 PATH,宿主经 --ffmpeg-location 注入可信路径——插件自带的同名参数仍在黑名单。
6. 失败语义:插件永远不能拖垮下载
- resolve 失败 → fail-closed,任务正常报错,绝不把网页 HTML 当视频落盘;
- 通知钩子(onStart/onDone/onError)全部 fire-and-forget:超时、报错只记日志;
- resolve 在 off-actor 线程跑,宿主单线程 actor 永不阻塞。
7. 去中心化插件市场
索引 = Git 仓库(任何人 fork 自建源),插件包内容寻址(sha256 钉住 zip 全文),多镜像 failover,per-index sequence 防回滚。v1 没有作者签名(schema 已预留字段),完整性基座 = 内容寻址 + TLS + Git Merkle DAG。
结语
完整实现都在仓库 native/engine/src/plugin/ 目录,欢迎 review 拍砖:
- GitHub:https://github.com/zerx-lab/FluxDown
- 官网 & 插件开发文档:https://fluxdown.zerx.dev
如果你有想适配的站点,评论区聊。
更多推荐
所有评论(0)