ARTICLE · 1352887
插件先跑,守卫后上班——关于DeepSeek Harness的插件体系安全
插件先跑,守卫后上班
——关于DeepSeek Harness的插件体系安全
11 月 4 日,开源的智能体运行框架 DeepSeek Harness 发布了 0.2.2-rc.1。两天后,我在 GitHub 讨论区那条 8 月 14 日提的问题下面补了一次复测的注释。快三个月过去,问题还在。

我做的实验很朴素。给这个框架一份 YAML 配置文件,在其中一行用 !!js 包一句打印语句,然后启动。那句话出现在启动日志里,位置在软件问「是否允许」之前,也在沙箱初始化之前。整个过程不需要绕什么弯子,配置文件里的表达式本来就设计成在启动时求值,求值地点是程序的主进程。
于是结论有点刺眼。一个插件想让代码跑起来而不必等用户点任何按钮。
我这篇要讲的就是这件事,以及它背后更硬的一层问题。我会先把机制走一遍,说清沙箱和审批各自管什么,再把边界与后果画出来,顺着一次安装把风险传导走到底,接着看 8 月到 11 月官方改了什么、没改什么,最后说说行业里最近发生过的几起真事和这个体系的关系。

图注|启动时序|表达式求值与插件加载发生在安全服务就位之前
一、一行 YAML 在启动日志里留下了什么
先把几个词交代清楚,后面就不再重复解释了。
主进程,可以理解成这个软件运行时唯一的那间屋子。你装的所有插件、模型要用的工具、保存密钥的凭据服务,都在这间屋子里,彼此之间没有墙。沙箱管不到这间屋子,它只覆盖屋子派人出去干活的那一小段路。审批属于交互流程,决定「模型想做某件事时,要不要弹窗问你一次」。配置树则是一份清单,写明这间屋子里有哪些组件、每个组件开什么参数、谁被关掉。
!!js 是 YAML 里的一种标签,作用是告诉解析器,跟着的这串东西要按表达式求值,不再当普通文本处理。它是写进官方文档的正式特性,不需要谁偷偷塞后门。官方的 Cordis 入门文档说得很明确,@deepseek-ai/cordis-plugin-include 把 !!js 解析为表达式节点,Loader 在声明的注入激活后,基于插件上下文插值条目的 config,并在每次挂载决策时插值 disabled 字段。翻成白话,配置里允许写活的计算式,好让同一个插件在不同环境里自动换参数、自动决定启不启用。这是个方便的功能。
方便的功能有个前提,就是写这段表达式的人可信。表达式在 Host 主进程里求值,而主进程手里握着文件系统、终端、网络与凭据。8 月 14 日我跑过一次无害的本地 PoC,把执行标记打进 stderr,确认 !!js 在任何防护激活之前就执行了,表达式能调用 node:child_process、node:fs 与 Node 的全局对象。11 月 6 日我在 0.2.2-rc.1 上重跑,--patch overlay 喂进去的 !!js 照样在 boot 期执行,求值栈的落点还是主进程里的 new Function。
YAML 标签能承载可执行表达式,是安全圈里很老的一类问题,编号 CWE-502,叫不可信数据的反序列化。2022 年的 SnakeYAML 漏洞 CVE-2022-1471 是其中最有名的一例,官方描述是 Constructor() 不限制反序列化时可实例化的类型,攻击者构造 YAML 内容就可能导致远程代码执行,通用漏洞评分为 9.8,修复建议是改用 SafeConstructor。JavaScript 生态里,js-yaml 的 load 与 safeLoad 之分也是同一个道理,OWASP 2025 版把它归进 A08 软件与数据完整性失效。
这里要区分一下。上面那些是解析库里的漏洞,这里是一段被明确允许的语法。老问题的处置经验已经很成熟,凡是可能来自外部的输入,一律走安全的解析路径,把能执行的东西挡在门外。而在这套框架里,配置文件是插件自带的,表达式是设计允许的,安全的解析路径从一开始就没有被当成需求。
于是那句打印语句才会安安静静地出现在启动日志里。从某种意义上来说,它不算是攻击,它只是这栋房子按图纸开的门。

图注|机制对照|老问题靠安全解析挡输入,这里把配置当成可信代码
二、把每个零件都做成可换的
要理解为什么会这样,得先看这个框架想解决什么。
DeepSeek Harness,命令行里叫 dsh,2026 年 8 月 13 日开源,MIT 许可,用 TypeScript 写成,底层是 Cordis 插件框架。它给自己定的口号就是「一切皆插件」。官方仓库的简介里,这句话是唯一的描述。
一个裸的大模型只会输出文字。要让它真能干活,得有人给它补上手、补上记忆、补上边界。手是读写文件和执行命令的工具,记忆是会话日志与上下文组装,边界是权限、审批与沙箱,另外还得有个循环驱动器,把「模型请求、工具调用、结果回填」这一串反复转起来。
多数编码助手把这些做成了产品里焊死的默认行为,用户挑一个成品用。DSH 的做法是把它们全拆成可替换的组件,模型适配器、工具注册表、系统提示词组装、会话日志、界面、沙箱、审批、凭据,连 agent loop 自己也是配置树里的一行。
这个设计有实打实的好处。研究团队可以把自己的模型路由换进去,企业可以把审计组件插到工具调用的前后,产品团队可以把同一套运行时嵌进自己的软件里。Cordis 还带来一条很讨人喜欢的性质,注册是可逆的副作用。插件通过上下文挂上去的工具、监听器、提示词片段,在卸载或重载时会自动撤销,不需要手工清理。
热插拔、配置回滚、插件启停因此成了默认能力,而不是要额外写一堆维护代码。
「可逆」这两个字容易被读多一层意思。它说的是拿掉的时候干不干净,不是运行的时候能不能被关在笼子里。Cordis 的默认信任模型沿自它原本服务的场景,那类聊天机器人插件跑在同一个进程里,权限与宿主一致,最坏的情况是机器人说错话。把同一套模型搬到一个能碰文件、终端、API 密钥和全部模型上下文的工具里,性质就变了。
打个不太精确的比方。原来那栋楼里每个房间都能换租户,租约到期清退得很干净,因为整栋楼本来只用来开会。后来楼里加了金库、机房和对外电话线,换租户的规则没改,房间还是那些房间,钥匙也还是同一把。清退流程依旧漂亮,可新租户进门的第一分钟,已经能走到金库门口了。

图注|配置树|安全组件与第三方插件是同一层里的普通条目
三、三个事实
把机制和政策分开看,问题就会落在三件很具体的事上。
第一件,安装不验身份。dsh plugin add 把参数原样转给 pnpm。pnpm 会校验包的内容有没有被篡改,这叫完整性校验,它不回答「发布者是谁」。官方插件市场文档补了一层说明,安装确认框会显示这一版的来源,是 npm 包与注册表,还是 Git 仓库、提交与目录。文档接下来那句更关键,索引是第三方内容,插件的标题、描述、图标、作者和来源都由索引决定,DSH 检查文件格式,但不验证这些声明,因此只应添加自己信任的索引源。
换句话说,界面只能告诉你包从哪来,但不能证明包里那层配置会改什么。我在原帖里还写了更细的一句,reconcilePlugins 只检查包是否声明 dsh.bundle.patch,据此把它加成配置层,没有信任判断。
第二件,代码在守卫上班前就已经运行。启动流程里,Loader 读取配置树,在 boot() 阶段通过 root.update 把每个插件条目实例化并跑它的 apply 与 init。智能体循环与交互界面要等 boot() 收尾之后才起来。审批与沙箱服务属于同一批被加载的条目,插件代码执行的那一刻,它们还没就位。我引的行号来自 8 月的 @deepseek-ai/dsh@0.1.0-rc.6 构建产物,官方源码可能会差几行,但顺序关系在 11 月的复测里我又确认过一次。
第三件,守卫的规则同样能被改。沙箱档位、审批策略、凭据存储,在配置树里就是普通的一行。插件自带的 patch 或命令行 overlay 可以在启动时把沙箱切到 danger-full-access,把审批策略改掉,甚至把某个安全组件直接置为 disabled。我用 --dump-config 看过合并之后的生效配置。原帖里还写了另一条路径,凭据、设置、沙箱、审批都是全局 Cordis 服务,插件在 init 里可以直接调用 ctx.credentials.set 一类方法写入本地凭据文件。
never 不是「不再询问,一律放行」。官方配置目录里的类型定义写得很清楚,ask 把审批请求交给组合好的应答者,没有应答者时按失败关闭处理;never 是谁都不问,每个请求一律解析为拒绝,这是给 CI 与无人值守运行准备的确定性姿态。官方权限预设表又把 danger-full-access 定义成「沙箱全开加 never」这组旋钮。0.2.2-rc.1 的发布说明里还有一条修复,说审批提示要明确说明是否向用户请求授权,避免模型误解 never 策略,可见这个歧义在实际使用中出现过。
弹窗确实会消失,但原因似乎不是 never 放行了全部操作。沙箱档位被切到绕过隔离之后,工具执行已经不再需要发起审批请求。真正拆掉围栏的是配置文件里那一行沙箱模式,审批策略只是不再有人来按门铃而已。
那沙箱到底管什么?官方子系统文档《进程沙箱》给的边界比很多人以为的窄。SandboxMode 仅管控文件系统效果,read-only 要求后端拒绝写入,workspace-write 允许在工作区根目录、显式额外目录与后端承诺的临时区域里写,danger-full-access 绕过隔离,网络与进程可见性不在这个定义的范围内。实现上,Linux 走 bubblewrap 或 Landlock,macOS 走 Seatbelt,Windows 走 ACL 受限令牌,包装的对象是与宿主共享执行环境的子进程 argv。
有一点官方做的也还算好,如果机器上找不到可用后端,执行就会抛出结构化的 SANDBOX_UNAVAILABLE 错误并拒绝运行,而不会悄悄降级成无约束执行,这已经是标准的失败关闭了。
所以,本质来看沙箱从定义上就管不到主进程。审批只能算是交互流程,不是隔离机制。三件事实合起来就只有一个结论,装一个插件,等于让一段陌生代码用你的权限、在安全检查生效之前先跑一遍,而且它能顺手把安全检查的设置改掉。

图注|三道闸门|每一道都有明确的管辖范围,也都落在插件能改写的配置树里
四、先画边界,再谈后果
还是把边界写清楚,我的批评才站得住。
这肯定不是零点击的远程执行。还是得需要你主动装上一个恶意的、或者被人投过毒的插件。控制范围也只限于运行 DSH 的那个用户账号。系统内核与同一台机器上的其他用户,都不在里面。当然,官方在安全告示里也没有含糊,该项目处于实验性的开发者预览阶段,没有经过安全审计,不能当成安全或可用于生产的软件;沙箱、审批提示与权限控制可以降低风险,但不保证隔离,也不保证不造成损害;更不要也不能把它当作处理不可信工作的唯一安全控制。
而在这个范围里,能拿走的东西约等于全部。该用户能读的文件都能读,SSH 私钥、.env、云服务密钥、浏览器配置、密码管理器数据库文件都在其中;可以执行任意命令,写启动项或计划任务做持久化;可以把数据发到任意地址;可以读写 DSH 的凭据服务,看到发给模型的全部上下文;每次启动都会重新跑一遍,直到插件被删掉。对一台开发机来说,这基本就是这台机器归它了。
更现实的入口不是有人专门盯着你,而是供应链。抢注一个和热门插件只差一个字母的包名,偷一个维护者账号,往热门插件里投毒,或者在合并请求里夹带几行配置。官方插件市场文档提醒只添加信任的索引源,前提是你能一直认得出什么是可信来源,这是过去两年最难做到的一件事。
同一份安全告示还给了用户侧建议,以最小权限运行,优先用一次性虚拟机、容器或专用环境,给项目能访问的文件留备份,不要把敏感凭据暴露给它,运行前审阅插件、配置与拟执行的命令。这些话写得很直白,也确实把信任模型摊开了。8 月 27 日与 9 月 3 日两个预发布版本的说明里,官方专门列出「更新安全告示,明确本项目未经安全审计、沙箱审批与权限不保证隔离」。
到 11 月 6 日,我直接在我当时提的 #587 Discussions 下面补了三条评论,帖子仍是 open,没有维护者的公开回应。
把这两件事并排放,已经不是简单的「没人管」;更贴近事实的是,项目在披露层已经做了该做的事,但机制层也丝毫没动。

图注|影响半径|范围止于当前用户账号,在该范围内几乎不留死角
五、从装进来到清不干净
前面列的是能拿走什么,没说这些东西一样样到手的路径。把 #587 只当成「插件权限太大」,会漏掉后半段。这条链上每一步都不需要额外的漏洞,也几乎不留下能顺着往回查的记录。
配置文件里的一行 !!js,在 Loader 读取配置树时被解析成表达式节点,交给主进程求值,落点是 new Function。不需要缓冲区溢出,不需要绕过校验,也不需要用户点确认。表达式拿到的是 Node 运行时的完整能力,node:fs 读文件,node:child_process 起进程,全局对象里还挂着当前进程的环境变量。官方 Cordis 入门文档把这写成正常特性,它本来就是为了让同一个插件在不同机器上自动换参数、自动决定启不启用。
求值的时间比地点更要紧。boot() 阶段 Loader 通过 root.update 把配置树里的插件逐条实例化,跑各自的 apply 与 init。智能体循环、交互界面、审批服务、沙箱服务都在同一批条目里,排在这段代码之后才初始化。守卫并非判定失误,那一刻它还没有被创建出来。
接下来是同一层里的横向移动。配置树把安全组件和第三方插件写成平级条目,@deepseek-ai/dsh-agent-loop 注入 agents、sessions、llm、tools、systemPrompt 等,@deepseek-ai/dsh-bash-sandbox 注入 subprocess、sandbox、sandboxPolicy,@deepseek-ai/dsh-credentials-local 管凭据。插件自带的 patch 或命令行 overlay 能在启动时改这些条目的 config 与 disabled。我把官方配置目录翻了一遍,可见内容里没有 protected、locked 一类安全标记,readonly 只是 TypeScript 的字段修饰符,跟权限无关。生效值是多层配置合并之后的结果,写在文件里的和跑起来的可以不一样,要看合并结果得用 --dump-config。
比改参数更细的一层,是改写模型看到的世界。Cordis 入门文档写明,提示词片段、工具 schema、适配器、提供方和监听器都通过 ctx.effect() 或 ctx.on() 安装,reload 与 teardown 时撤销。ctx.waterfall 是环绕中间件,监听器拿到 (...args, next),可以包装下游返回值,也可以不调用 next() 直接短路,官方文档还允许监听器完全替换结果,让下游只看到替换后的内容。把这几条放在一起读,一个插件能往系统提示词里加一句,能在工具返回给模型之前改一遍,也能在 approval/request 这条 waterfall 上抢先给一个决策。
这里得替审批子系统说句公道话。它的设计相当严密。ApprovalOutcome,也就是审批结果,只有允许一次、拒绝、取消、不可用四种,失败时一律按拒绝处理,allowed-once 仅授权被询问的那一个操作;setApprovalPolicy 是改写审批策略的唯一路径;never 在服务内部、waterfall 分发之前就强制执行,后来用 prepend 抢到优先级的应答者也绕不过去。这套严密覆盖的是走到审批请求这一步的调用。启动期代码不必发起审批请求,它本来就在服务加载之前。
剩下两件事没有悬念。官方进程沙箱文档把 SandboxMode 限定在文件系统效果上,网络与进程可见性不在这个定义范围内,主进程发起网络请求不需要经过任何一道审批,外发通道是敞开的。持久化也简单,写启动项、计划任务,或者往某个工具的配置文件里塞一行,下次启动配置树重新加载,表达式再求值一次。
六步走完,装插件这个动作已经结束了很久。
更难回答的是后面那个问题,做完之后,机器会留下什么。
启动日志里可能多一行输出,也可能一行都没有,那句打印语句只是我为了确认时机才放的。会话记录与工具调用列表覆盖的都是 agent loop 起来之后的动作,审批子系统管的则是走到询问这一步的调用。官方审批子系统文档把 setApprovalPolicy 写成唯一写入路径,理由是回放要能重建覆盖值;启动期代码不进入这条交互流程,自然不会有对应条目。
可逆副作用在这里帮了倒忙,ctx.effect() 注册的东西在 teardown 时按预期撤销,运行期不留常驻对象,卸载之后连「这里曾经挂过什么」都得靠配置文件反推。终端防护看到的则是 dsh 这个合法进程在读自己的配置、连自己的网络。币安那条链上被替换的正版开发工具组件,恶意版本只比它多 70 字节,同样是把自己藏进一个本来就该运行的文件里。
于是常规的处置顺序会出问题。
卸载插件撤销的是 Cordis 注册,撤不掉已经写进启动项的脚本、已经发出去的数据、已经在别的系统上生效的授权。轮换凭据也不覆盖这些。币安 10 月 29 日事件里,6 月那次处置换过设备、轮换过全部相关凭据,10 月攻击者仍在运维工程师的机器上找到容器部署令牌。
Cursor 在 10 月 29 日的安全公告里直接提醒,仅仅卸载扩展可能不够,载荷会留下独立运行的持久化脚本,建议隔离主机、清除残留、从可信系统轮换凭据与会话。5 月那份国家网络安全通报中心的通报要求排查 Claude Code hooks 与 VS Code 任务配置,理由是卸载依赖之后恶意代码还会继续残留。
三份材料说的是同一件事,处置对象不能停在那个包上。
得把单位从「这个插件」放大到「这台机器、这个账号、这个身份能碰到的所有下游」。币安最后是在新环境重建受影响的钱包系统,也重建了那款内部 AI 工具及其访问控制,吊销相关凭据与令牌,对全员设备做增强扫描。选重建而不是修,是因为被改过的信任关系没有一个能靠回滚配置恢复。以太坊 EIP-7702 的委托授权写在链上,等于把热钱包的部分执行权交给一份攻击者控制的合约,暂停提现和关停热钱包都撤不掉,只能另做一套撤销流程,这是外部副作用不可回滚最直白的一个例子。
我在 #587 里写的是第三方插件在启动期拿到配置树的完整写权限,听起来像一条实现层面的缺陷,补几行判断就能收口。顺着这条链走一遍会发现,求值时机、平级的配置树、可包装的事件、不受管的外发、只记交互的日志、只撤销注册的卸载,是同一个信任模型的六个出口。焊死其中一处,其余五处照旧。
装插件在这里的含义,是把一份可执行的信任交给陌生代码,交接发生在验收之前。

图注|风险传导|守卫在第二步之后才就位,常规处置只覆盖注册撤销
六、一周前,另一台开发机上真的发生了
如果上面的场景还显得像推演,10 月 29 日的一起事件可以直接拿来对照。
Cursor 官方发布的安全公告说,当天凌晨 2 时 27 分到下午 1 时 35 分(太平洋时间),其扩展市场里出现了被篡改的 anthropic.claude-code 扩展,涉及 2.1.312 与 2.1.313 两个版本的 Windows 与 macOS 包,最多影响 5000 名用户。这两个版本不是 Anthropic 做的。
研究者在扩展主 JS 包里发现插在合法代码之前的 66 行加载器,在扩展加载时就执行,不需要用户开启任何 AI 对话。它往 ~/.ssh/authorized_keys 追加一条标记为 svc-telemetry 的攻击者公钥,尝试开启本地 SSH 服务并建反向隧道,用 macOS 的 LaunchAgent、Windows 计划任务或 Linux 定时项做持久化,约每 60 秒重试一次连接,同时把主机名、用户名、系统与架构、主目录、Git 邮箱、公网 IP,以及最多 40 个名字里带 KEY、TOKEN、SECRET、PASS、AWS、GCP 的环境变量名发出去。
有几个细节值得对照着读。开启本地 SSH 服务在多数 macOS 与 Windows 机器上需要管理员权限,恶意代码这一步多半失败,Cursor 也说明了这一点。可公钥、持久化和信息回传都成功了,官方因此在公告里提醒,仅仅卸载扩展可能不够,载荷会留下独立运行的持久化脚本,建议隔离主机、清除残留、从可信系统轮换凭据与会话。Anthropic 随后在 Open VSX 与微软扩展市场发布 2.1.314,据 The Hacker News 与 Socket 的核查,恶意版本只经过 Cursor 市场分发。11 个小时的窗口,一次常规更新,一个看上去完全正常的发布者身份,就够了。
把时间轴拉长,这类事在过去一年多里是连续的。2025 年 9 月,自复制的 Shai-Hulud 蠕虫通过被攻陷的维护者账号进入 npm,用安装脚本搜刮 GitHub 令牌与 AWS、GCP、Azure 密钥,再用窃取的发布权限给同维护者的其他包投毒。CISA 在 9 月 23 日的警报里说超过 500 个包受影响,建议审查依赖、固定到 9 月 16 日之前的版本、轮换全部开发者凭据并强制抗钓鱼的多因素认证。
同年 11 月出现的第二波被研究机构称为 Shai-Hulud 2.0,据 Unit 42 的统计,波及约 350 个用户、产生超过 25000 个用于存放窃取数据的仓库,且在拿不到凭据时会尝试抹掉用户主目录。这一段数字来自安全厂商的公开分析,我没有拿到原始数据集,按转述标注。
2026 年 5 月 25 日,国家网络安全通报中心通报了同一家族的新一波投毒,涉及 300 余个独立程序包的 600 余个恶意版本,点名 echarts-for-react、@antv 系列核心库、TanStack 系列 42 个包等受影响项目。通报的处置建议里有一条与本文特别相关,要求排查 Claude Code hooks、VS Code 任务配置等位置,检查是否存在 router_runtime.js、setup.mjs 一类可疑文件,避免卸载依赖之后恶意代码继续残留。SANS Internet Storm Center 在 5 月 25 日的日志里给出独立统计,这一波是 323 个包、639 个恶意版本,并记录了 .vscode/tasks.json 与 ~/.claude/settings.json 被用作持久化落点。AI 编程工具的配置文件,已经成了供应链攻击常规检查项。
规模也不是偶发。GitHub 官方在一篇解释依赖更新冷却期的博客里给出数据,截至 2026 年 5 月的一年内,GitHub 公告数据库收录了 6500 多条 npm 恶意软件公告,比上一年的约 6200 条还多,平均每天约 18 个新收录的恶意包。同一篇里举了 2025 年 9 月 chalk 等包被投毒的例子,恶意版本从发布到被撤下只有大约两小时,而两小时足够自动更新工具把新版本推进你的拉取请求。
这就是 GitHub 把 Dependabot 版本更新默认冷却期设成三天的理由,安全更新仍然立即开,普通更新等一等,让检测信号先跑一会儿。

图注|时间线|插件与扩展的信任问题屡次出现,节奏在加快
七、四个月,三台电脑,一个令牌
10 月 29 日还有另一起。当天晚上,币安的热钱包开始向外转出资产。11 月 3 日公布的初步调查说,约 63.4 亿美元数字资产受影响,约 28.5 亿被冻结或拦截,截至 10 月 31 日已补足 98% 的受影响代币资产,损失由平台自行承担。币安援引美国联邦调查局的信息,把幕后指向朝鲜。
起点在四个月前的一台开发机上。
2026 年 6 月,币安一名负责钱包基础设施的运维工程师想装一款热门 AI 编程工具,在搜索引擎上找到并下载了被篡改的 Homebrew 安装程序。Homebrew 是 macOS 上常用的包管理器。安全团队发现异常,换掉设备,轮换全部相关凭据,按标准流程收了口。攻击者已经拿走另一样东西,这名工程师的岗位,以及钱包基础设施团队里有哪些同事。设备能换,凭据能轮换,组织关系换不掉。
9 月,第二名工程师在正规求职平台被人联系上,他的资料里提到在币安工作。第一轮是 HR 视频面试,面试官拿出带生活照和视频的社交账号建立信任。第二轮转到聊天应用,对方自称一家虚构公司的首席执行官,面试快结束时让他把一份「作业」代码仓库下载到工作电脑。恶意代码在仓库的安装阶段执行,把一个合法开发工具组件换成恶意版本,连上攻击者控制的服务器,再清掉痕迹。这份当时还没有特征可查的恶意软件,没被终端防护检出。
报告里贴了那段脚本。它删掉正版的 pyenv,把 3,414 字节的恶意文件改名放回同一路径,重建符号链接,再拉起一个 shell。pyenv 是管理 Python 版本的工具,很多终端一打开就会加载它。正版是 3,344 字节,两者只差 70 字节。替换一个已经存在的文件,比往机器上扔一个新文件更不容易被注意到。
10 月 20 日,币安在手法概述里用了「被攻陷的内部 AI 工具」这个说法,时间线给的定位更具体,小标题是「把一款内部 AI 工具用作跳板」。攻击者从第二名工程师被控制的电脑出发,借这个工具拿到运维工程师和第三名钱包软件工程师设备的控制权,并在运维工程师的机器上找到一个容器部署权限令牌。四个月前那次处置轮换过凭据,令牌仍在那台机器上。安全公司 Cyera 提醒,币安没有披露这个工具具体是怎么被使用的,目前也没有证据显示有被投毒的智能体技能参与了这条链。
10 月 29 日,按世界协调时,17 时 15 分到 19 时 56 分之间,攻击者登录第三名工程师的电脑,用运维工程师的凭据部署容器、触发恶意脚本,触达内部钱包签名服务。20 时 05 分起的四分钟里,五个热钱包向外转出约 2.33 亿枚 XRP,随后还有约 97.05 万枚 SOL、4,420 万枚 OP 与 18,270 枚比特币。攻击者又借签名服务滥用以太坊的 EIP-7702 委托授权,让一份攻击者控制的合约拿到热钱包的部分执行权,授权写在链上,暂停提现和关停热钱包服务都撤不掉,币安只能另做一套撤销流程。最后一笔未授权转账在 23 时 56 分完成。
币安没有说内部 AI 工具本身有漏洞,也没有说它被塞进恶意插件。入口是伪装成热门编程工具的篡改安装包,和被诱导在工作电脑上运行的作业代码。内部 AI 工具是把一台机器的失陷放大成三台机器的那一级。
币安事后在新环境重建了受影响的钱包系统,也重建了那款内部 AI 工具及其访问控制,吊销相关凭据与令牌,对全员设备做了增强扫描。报告里还有一句,2026 年全年他们拦下 57 起疑似朝鲜关联的网络威胁,手法包括伪装成热门开发工具的恶意软件、被攻陷的软件包与浏览器扩展。这串清单和 10 月 29 日那个被篡改的 Cursor 扩展,是同一类入口。

图注|币安事件攻击链|一台被投毒的开发机,如何沿着可信工具放大到钱包签名服务
这件事,其实很容易就能让我们想到,如果把这台机器换成一套运维用的 AI 工具,链条会短很多。
这套插件体制让 DSH 适合被当成入侵框架来用。入侵所需的几样东西,恰好都是它的正常功能。
harness 指的是让模型能读文件、跑命令、调内部接口的那层运行框架,DSH 正好是做这个的。运维场景里它天然要握部署令牌、云账号密钥、SSH 配置与内部服务地址,还要连得上持续集成、容器编排和签名服务。币安那条链上,攻击者花四个月、两次社工、三台电脑才走到钱包签名服务,最费力的两件事,是先在一台工程师电脑上拿到代码执行,再把这个执行权限放大到另外两台。
最难的两步,这套架构已经预先替攻击者做掉了。
拿到第一台机器的代码执行,不必再靠假招聘。官方插件索引文档写明,索引是第三方内容,标题、描述、图标、作者和来源都由索引决定,DSH 检查文件格式,但不验证这些声明;索引可以放在任意静态主机上,用 http: 取回时,网络路径上的任何人都能改动其中列出的安装地址。安装确认框显示这一版的来源,是 npm 包与注册表,还是 Git 仓库、提交与目录,不显示包里的配置层会改哪几行。
第三节说过的 dsh plugin add 直接转发 pnpm,校验的是包内容有没有被篡改,是同一道门的另一扇。官方要求只添加信任的索引源,也写明未经确认不会安装任何内容,这些控制都在,它们管的是「你知不知道自己在装谁」,不管「这个包装进来会做什么」。
从一台机器走到另外两台,也不必再做横向移动。第三方插件一进主进程就有宿主的完整权限,boot() 期求值早于审批与沙箱,配置树里安全组件和插件平级,dsh.bundle.patch 或 overlay 能改它们的 config 与 disabled。
运维 harness 要用的凭据走官方的 dsh-credentials,设计上配置里只放环境变量名这样的引用,真实值由提供方保管,resolve(ref) 每次操作返回明文值,set 还能把值持久写回去。这道隔离挡的是配置文件与配置界面,describe 刻意不返回值,它挡不住同一进程里的插件,第三节记过 ctx.credentials.set 在 init 里就能直接调用。币安的容器部署令牌留在工程师的笔记本上,要靠横向移动去翻;放进这套 harness,它本来就在模型与工具的调用路径上,等着被解析。
再往后的放大,是同一间屋子里的事。网络不在 SandboxMode 定义范围内,主进程发请求不经过审批;ctx.waterfall 允许监听器包装或替换工具返回,模型看到的执行结果可能是被改过的;提示词片段、工具 schema、适配器都能在 init 里注册。币安的攻击者要登录第三名工程师的电脑、拿别人的凭据部署容器、触发脚本,才触达签名服务。运维 harness 把这些动作收在同一间屋子里,插件代码启动时就已经站在屋里,而启动期代码不进审批日志,卸载插件也只撤销注册。
换个角度看,入侵者平时得自己凑齐的东西,这里几乎都是产品功能。要一个每次开机自动重跑的落点,配置树的启动加载就是;要读密钥,凭据服务按操作把明文递给调用方;要把数据送出去,主进程发网络请求不受沙箱管辖,终端防护看到的只是一个合法开发工具在联网;想少留痕迹,ctx.effect() 挂上去的注册在卸载时会干净撤销,启动期代码又不进审批日志;想加新能力,「一切皆插件」允许把任意逻辑做成组合包,顺着索引分发。
币安那条链上,攻击者要自己写没特征可查的载荷、替换 pyenv、连回自己控制的服务器,还得躲过终端防护。换成这套框架,这些活儿由宿主替他做了,连怎么躲终端防护都不用另想,因为这款工具本来就该出现在一台运维机器上。
币安没有披露那款内部 AI 工具的具体用法,Cyera 也指出没有证据显示投毒技能参与,上面是机制推演,跟那起事件的归因无关,也没有说 DSH 已经被用进真实入侵。可这个判断不受影响,一款把持久执行、凭据读取、任意外发和插件分发都做进正常功能的框架,天然比普通软件更适合被拿来当入侵工具。前提是它得被授予生产凭据与部署权限,还得装上被投毒的插件;官方建议一次性环境,就是不让这两条同时成立。
DSH 把信任写在架构里,币安把信任写在流程与权限配置里。工程师可以自装工具、作业代码能在工作电脑上跑、部署令牌留在个人笔记本、内部 AI 工具能触达同事设备,是一串分别做出的授权决定;DSH 把它们合成了一个动作。运维该在生产凭据与第三方代码之间画一道墙,这套架构里没有,也没有哪个配置项能补上。入侵者要找的就是这样一间屋子,装得进自己的代码,拿得到别人的凭据,出门还不被拦。

图注|四个月对一次安装|币安四个环节,在运维 harness 上各对应哪一处信任缺口
八、37 个版本之后,什么没变
回到 DSH。11 月 6 日我在 0.2.2-rc.1 上逐条复测,三条路径都还在。
启动期 !!js 仍由 new Function 在主进程求值,发生在任何审批或沙箱初始化之前。overlay 仍能把 sandbox-policy.mode 改成 danger-full-access,把审批策略改成 never,--dump-config 能看到合并结果,这几项在 0.2.2 的 dsh-base 里仍是普通可组合行,没有 protected 或 locked 语义。dsh plugin add 仍直接转发 pnpm,没有签名、来源或配置差异校验。
说「什么都没做」不公平。按 GitHub 的版本记录,从 8 月 17 日的 0.1.0-rc.7 到 11 月 4 日的 0.2.2-rc.1,仓库留下 37 个预发布版本,迭代密度很高。0.2.x 补上了插件管理页与富文本详情、安装前查看元数据、组合包详情页显示代码来源与当前版本、Git 插件带 prepare 脚本时改为显式批准并支持重试、安装失败给出完整诊断与 Host 日志、第三方插件更新失败时尝试恢复旧版本。通过智能体的 plugin_manager 工具安装,现在需要 danger-full-access 或逐次审批。
最后这条有分量,它挡住的是「模型被提示注入之后自己去装插件」这条链,把安装动作从模型手里拿回到人手里。0.2.2-rc.1 还修了一个权限相关的问题,关闭应用时 Auto 会话权限曾意外变成完全访问,现在保留已保存的权限与审批策略。
逐条看完这 37 条发布说明,与安全有关的条目主要是两类,一类是更新安全告示,明确未经审计、沙箱审批不保证隔离,另一类是更新运行时工具、MCP 协议集成与依赖包以带上兼容的安全修复。只是这里面没有一条针对插件加载期信任模型的修复说明。
把这些改动归归类就清楚了。它们管的是「谁触发安装」与「装之前能看到什么」,没改「装进来就在主进程里全权执行」。可发现性、人工控制、失败诊断都在变好,信任层还是原来那一层。
8 月 16 日我在讨论里补过一段更结构化的话,开放的组合原则乘上可信的进程内插件模型,前者允许任何插件重写任何组件,后者假设所有插件可信,两者叠加,安全边界在架构上就不成立,缺的不是某个实现细节。

图注|两栏对照|治理层管住安装动作,信任层决定装进来之后发生什么
九、这不是一个 bug
把问题说成「生态还没成熟」,因果是反的。生态是结果,不是原因。
真正的矛盾在两条设计前提之间。一边是「一切皆插件」的开放组合原则,它要求任何组件都能被替换,包括安全组件。另一边是「插件都是可信的进程内代码」的实现假设,它要求所有替换者都值得信任。两条同时成立,安全边界在架构上就不存在。某个版本号之后补一行判断,改不了这个结构。
有个框架可以帮着看清楚后果。安全研究者 Simon Willison 在 2025 年 6 月给智能体总结过一个叫「致命三要素」的组合,能读私有数据、会接触不可信内容、有把数据发出去的通道,三样凑齐在同一段上下文里,一次成功的提示注入就可能变成一次数据外泄。他强调这是用来判断配置的分析框架,与某个具体漏洞无关,也没有补丁可打,办法是拆掉其中一条腿。
放进 DSH 的场景,私有数据是凭据文件与全部模型上下文,不可信内容可能是插件自带的代码,也可能是模型从网页、仓库、issue 里读到的文本,外发通道则是主进程随手就能发起的网络请求,而官方沙箱文档白纸黑字写着网络不在 SandboxMode 的定义范围内。三条腿在一个进程里凑齐了。
2026 年 10 月的 Cursor 事件还说明了一件事,安装动作正在从人手里滑向自动化。扩展会自动更新,智能体会自己调用工具装插件,配置会随仓库一起被克隆下来。DSH 0.2.x 给 plugin_manager 加门控,正是对这个变化的正确反应。只是门控管住了起点,管不住进门之后那间没有墙的屋子。
要修,得动信任层。我在讨论里前后提过五条方向。加载第三方代码之前先冻结安全策略;安全配置项不可被普通插件覆盖;插件改为进程外运行,用能力与远程调用的模型替代同进程全权限;安装时展示权限与配置变更,而不是只给一个包名;再加上签名与信誉机制。
前两条是最小改动,第三条是架构改动,后两条是把人拉回决策现场。

图注|修复阶梯|把安全策略锁在插件进来之前,是这条路径上的第一级
十、信任层,以及四种不同意见
批评要站得住,得先把几种不同意见摆出来。
第一种意见有它的道理。项目自己写着开发者预览、未经安全审计、会有破坏性变更,安全告示甚至建议用一次性虚拟机和专用环境。拿生产软件的标准去要求一个三个月大的预览版,确实过头。
我的看法是,这条意见能解释优先级,不能解释边界的形状。预览版可以缺功能、可以改接口,但「第三方代码在核心进程里全权执行」不会随版本成熟自动消失,它需要一次明确的架构决定。8 月 27 日那份安全告示把边界讲清楚了,这是负责任的做法;讲清楚之后,边界还在。
第二种意见也有事实依据。插件全权限不是 DSH 独有。微软在 VS Code 工作区信任的官方文档里直接写着,工作区信任挡不住恶意扩展执行代码并忽略受限模式,因此只应安装来自知名且可信发布者的扩展。
Figma 走了另一条路,官方插件文档说明,插件代码跑在一个最小 JavaScript 沙箱里,没有 DOM、fetch、XMLHttpRequest,要访问浏览器接口得另开 iframe 并用消息传递通信,插件能访问的网络域名由 manifest 限制。Figma 后来因为 Realms 垫片被曝漏洞,把实现换成编译到 WebAssembly 的 QuickJS 虚拟机,官方博客承认新方案对某些插件更慢、调试不如从前方便,但隔离更强。这两家把两种代价都算过一遍。一种把信任交给发布者,一种把边界建在运行时。
第三种意见最容易被高估,就是签名与信誉能解决问题。注册表侧这两年确实在快速收紧。npm 官方文档现在把可信发布写成推荐路径,用 OpenID Connect 让 CI 以短时凭证发布,不再存长期令牌,并且在公共仓库发布公共包时自动生成来源证明。
GitHub 在 2026 年 7 月 28 日的官方总结里列出一整套动作,5 月上线分阶段发布,要求额外批准与双因素认证;6 月宣布的 npm v12 把安装脚本改成默认关闭,同时默认挡掉通过 git 或远程 URL 引入的依赖;7 月给 Dependabot 版本更新加三天冷却;高影响账号在改邮箱或使用双因素恢复码时进入 72 小时只读;还提供自助吊销全部凭证的工具。
这些改动解决的是「谁有资格发一个包」和「装的时候要不要立刻执行脚本」,都很有价值。但它们回答不了「一个合法身份发出来的包里写着什么」。2026 年 5 月那波投毒里,安全厂商记录到被劫持的发布流水线生成了有效的 SLSA Build Level 3 来源证明,另有几十个包在 npm 界面上显示伪造的 Sigstore 验证徽章。据 StepSecurity 的分析(经安全厂商公开报告转述),来源证明能证明哪个提交被构建,不能证明这个提交被授权。签名是链条上的一环,不是链条本身。
第四种意见更激进一点,认为「安装前弹个确认框」这道控制太弱。人在看不懂配置差异的时候点同意,和没有这道框差别不大。这一条我倾向于同意,但它是我的判断,不是现有材料能证明的结论。官方插件市场文档已经把确认框能显示什么写清楚了,显示来源,不验证索引声明,也不展示配置层会改哪几行。把「会改沙箱模式」「会关掉审批」这类差异摆到确认框里,成本不高,效果比再叠一层签名更直接。
趋势上我认为会往两边走。一边是注册表与分发渠道继续收紧身份、延迟新版本进入、默认关掉安装期执行;另一边是运行时把第三方代码请出核心进程,或者至少把安全策略锁在普通配置树之外。前者在过去一年已经有明确进展,后者在预览版框架里还没有看到路线图。对普通用户来说,判断一个 AI 编程工具值不值得托付,多看一个问题就够了,装进来的第三方代码,究竟跑在哪一间屋子里。
十一、现在能做的事
在架构改动之前,能做的都是些不新鲜但有效的动作。
只装可信来源的插件,安装前看它的 cordis.patch.yml 与 package.json,尤其留意有没有声明 dsh.bundle.patch。把 DSH 跑在容器或独立用户账号里,给它单独的 DSH_HOME,别让它以管理员权限启动。定期检查 ~/.dsh/profiles/*/package.json 的依赖,锁文件有变动时看差异而不是只看版本号。凭据不要放在这个账号能随手读到的位置,定期轮换。
网络出方向能收紧就收紧,官方沙箱文档已经说明网络不在 SandboxMode 管辖内,这一层只能靠环境自己补。用 --dump-config 看生效配置,比看写了什么配置更可靠。
如果曾经装过来路不明的插件,卸载之后还要查启动项、计划任务与各类工具配置,2026 年 5 月那份通报专门提醒过这一步。
也要说清我手里没有什么。行号来自 8 月的 0.1.0-rc.6 构建产物,我没有逐行对照最新 master;PoC 是无害的本地验证,只确认了执行时机、模块能力和配置改写,没有做破坏性载荷。社区后来又提过插件静默替换宿主界面、Git 钩子执行任意代码、web_fetch 的 SSRF 一类相邻问题,我没有逐条独立复核,所以不展开。维护者是否计划调整信任层,公开讨论、文档与发布说明里都没有答案,我不知道,也不替他们猜。8 月发帖时我写过一句,能有维护者私下跟进会更欢迎,也愿意帮忙细化或做原型验证,这几行到今天还挂在原帖末尾。
写这些不是为了唱衰。这个项目的可组合性做得相当漂亮,「一切皆插件」是句有野心的宣言,可逆副作用、能力拆分、会话可重建这些设计,对想自己搭智能体的团队有真实价值。问题只在于,宣言和信任模型得对齐。要么把插件请出核心进程,要么在它进来之前把安全策略锁死。到 0.2.2-rc.1 为止,两件事都还没做。
对一款只能聊天的软件,装插件是一次功能选择。对一款能读文件、跑命令、碰到凭据、还能自己决定下一步的软件,装插件是一次信任决定。把这两件事当成同一件事来处理,才是我 8 月发帖、11 月又回去复测真正想说的地方。
资料来源|官方仓库与文档、我在 GitHub Discussions #587 的原帖与三条补充评论、监管机构通报、厂商安全公告与公开研究,详见下方信源清单。
参考链接
DeepSeek Harness 官方仓库 README 与 SAFETY.md / SAFETY.zh.md(开发者预览、未经安全审计、沙箱审批不保证隔离、最小权限与一次性环境建议),https://github.com/deepseek-ai/deepseek-harness 官方 Cordis 入门文档(Loader 配置、 !!js表达式节点、config与disabled插值、ctx.effect()可逆注册、waterfall 包装与短路、结果可完全替换),https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/cordis-primer.zh.md官方进程沙箱子系统文档(SandboxMode 仅管控文件系统效果、各平台后端、SANDBOX_UNAVAILABLE 失败关闭),https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/sandbox.zh.md 官方配置目录(权限预设表、 ApprovalPolicy的ask与never类型定义及注释、各组件 inject 列表、可见内容无 protected 与 locked 安全标记),https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/config-catalog.zh.md官方审批子系统文档( ApprovalPolicy的ask与never定义、ApprovalOutcome闭合且失败时拒绝、allowed-once仅授权单次操作、setApprovalPolicy唯一写入路径、never在 waterfall 分发前强制执行、approval/requestwaterfall 与回放记录),https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/approval.zh.md官方用户凭据子系统文档(凭据 seam 把机密挡在配置之外、settings 与 cordis.yml只放环境变量名引用、resolve(ref)每次操作返回明文值、describe刻意不暴露值、set可持久写入、本地提供方的env/file/project-env/user-env来源层),https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/credentials.zh.md官方插件索引指南(安装确认框只显示来源、索引为第三方内容且 DSH 检查格式但不验证声明、索引可托管于任意静态主机、 http:索引的安装地址可被网络路径修改、未经确认不会安装、安装时做兼容性与注册表检查),https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/user/guide/plugin-market.zh.mdGitHub Discussions #587「[security] Third-party plugins get full config-tree write access at boot...」,本人 2026-08-14 发布,含 8 月 14 日、8 月 16 日与 11 月 6 日三条复测与补充评论,https://github.com/deepseek-ai/deepseek-harness/discussions/587 GitHub Releases 全部 37 条预发布说明(0.1.0-rc.7 至 0.2.2-rc.1,含 8 月 27 日与 9 月 3 日安全告示更新、0.2.2-rc.1 权限修复),https://github.com/deepseek-ai/deepseek-harness/releases 币安官方初步调查报告,2026-11-03(6 月为装 AI 编程工具下载被篡改的 Homebrew、9 月虚假招聘与 pyenv 替换脚本、10 月 20 日内部 AI 工具作跳板、10 月 29 日 UTC 时间线与 EIP-7702 授权、63.4 亿与 28.5 亿及 98% 补足、全年 57 起拦截、整改清单),https://www.binance.info/ru-KZ/square/post/384312159834579 币安官方《Security Incident Update》,2026-10-30(初步判断涉及 DevOps 工程师笔记本失陷、无证据显示钱包私钥泄露、预估约 63 亿美元、早期口径约 30 亿链上冻结),https://www.binance.com/en/support/announcement/detail/c5a8729a97ac486d963784d96c5642d7 Cyera Research 关于币安事件的分析报告,2026-11-06(指出币安未披露 AI 工具具体用法、无证据表明投毒技能参与、恶意版 pyenv 比正版多 70 字节、部署令牌的处置边界),https://www.cyera.com/research/binances-63-billion-heist-ran-through-an-internal-ai-tool 中国经济网转载《国际金融报》报道,2026-11-05(中文可核的虚假招聘、内部 AI 工具与整改进展转述),https://finance.ce.cn/stock/gsgdbd/202611/t20261105_3284794.shtml CISA 警报「Widespread Supply Chain Compromise Impacting npm Ecosystem」,2025-09-23(Shai-Hulud 超过 500 包、凭据搜刮与处置建议),https://www.cisa.gov/news-events/alerts/2025/09/23/widespread-supply-chain-compromise-impacting-npm-ecosystem (全文另见 https://community.opentextcybersecurity.com/topic/show?tid=362115) GitHub 官方博客「Disrupting supply chain attacks on npm and GitHub Actions」,2026-07-28(分阶段发布、npm v12 默认禁用安装脚本、Dependabot 冷却、账号只读与凭据吊销),https://github.blog/2026-07-28-disrupting-supply-chain-attacks-on-npm-and-github-actions GitHub 官方博客「The case for a cooldown」,2026(截至 2026 年 5 月一年 6500 余条 npm 恶意软件公告、约每天 18 个、chalk 事件约两小时、三天默认冷却),https://github.blog/security/supply-chain-security/the-case-for-a-cooldown-why-dependabot-now-waits-before-issuing-version-updates npm 官方文档「Trusted publishing for npm packages」(OIDC 短时凭证、自动生成来源证明、CLI 与 Node 版本要求),https://docs.npmjs.com/trusted-publishers/ Cursor 官方安全公告「Security notice, malicious version of Anthropic.claude-code」,2026-10-29(2.1.312 与 2.1.313、时间窗口、最多 5000 用户、持久化与处置建议),https://cursor.com/blog/claude-code-extension-security-notice The Hacker News「Official Claude Code Extension on Cursor Marketplace Trojanized with Persistent SSH Backdoor」,2026-10(66 行加载器、回传字段、2.1.314、仅经 Cursor 市场分发),https://thehackernews.com/2026/10/official-claude-code-vs-code-extension.html 国家网络安全通报中心通报,2026-05-25,经中国新闻网、证券时报转载(300 余个独立程序包、600 余个恶意版本、排查 Claude Code hooks 与 VS Code 任务配置),https://m.chinanews.com.cn/wap/detail/chs/zw/jw672042.shtml SANS Internet Storm Center 日志,2026-05-25(@antv 波次 323 包 / 639 恶意版本、 .vscode/tasks.json与~/.claude/settings.json持久化、伪造 Sigstore 徽章),https://www4.dshield.org/diary.html?date=2026-05-25微软 VS Code 官方文档「Workspace Trust」(受限模式边界、明确说明挡不住恶意扩展),https://code.visualstudio.com/docs/editing/workspaces/workspace-trust Figma 官方插件文档「How Plugins Run」与官方博客「An update on plugin security」(最小 JS 沙箱、iframe 消息传递、manifest 域名限制、改用 QuickJS-WASM 的代价),https://figma.com/plugin-docs/how-plugins-run/ MITRE CWE-502 与 NVD CVE-2022-1471(SnakeYAML 不安全反序列化、CVSS 9.8、SafeConstructor 修复建议),https://cwe.mitre.org/data/definitions/502.html Simon Willison「The lethal trifecta for AI agents」,2025-06-16,以及公开分析对该框架的整理(私有数据、不可信内容、外发通道),https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/















































































