深入剖析 Claude Code Mods 的双层沙箱设计:语言级沙箱限制代码通过 $ API 访问外部资源,但 Mod 仍可执行任意代码——真正信任边界在人工授权而非技术隔离。
关于 Claude Code mods,Anthropic 官方文档里有两段描述,且均为事实:
模块运行在一个独立的沙箱中,没有 DOM,也没有 Node。
"Mods 没有沙箱隔离。"
mods 上线头两天,全网都在争论上面哪句话是假的。其实两句都是真的。问题出在 "sandbox" 这个词身上——它同时指代了两件不同的事。一旦把两者分开,真正的信任边界就清晰了。它不在大多数人以为的位置上。
当你写一个 mod 时,你的模块得到一个私有的小世界。没有 document,没有 window,没有 Node 全局变量。如果你的代码想接触任何外部的东西——文件、进程、网络、UI——都要通过一个对象:$。
export function register(on) {
on("tool.call", { tool: "Bash" }, async ($, e, next) => {
const listing = await $.fs.readDir("/tmp"); // through $, not fs
return next(e);
});
}
这是沙箱 #1。它是一个语言层面的沙箱:约束你的代码如何访问外部世界,而不是能否访问。每项能力都藏在 $ API 后面,这意味着引擎可以看到每一个请求、记录它、(理论上)可以拦截它。可以把它想象成一部手机,所有 App 都必须使用官方 API。但这些 API 仍然包含了摄像头。
文档里是这么说的,我原文引用因为意译会削弱其力度:
"mod 是以你的权限运行的代码。它可以读取和写入你的文件、启动进程、发起网络请求。只安装来自你信任的作者和市场的 mods。"
"Mods 没有沙箱隔离。如果开启沙箱功能,沙箱会隔离 Claude 运行的 Bash 命令,而 mod 启动的进程运行在沙箱之外。"
把第二句读两遍。Claude Code 有一个沙箱功能,但它隔离的是 Claude 的 Bash 命令。一个 mod 如果启动自己的进程,会干净利落地跳出沙箱。围栏是围着 Agent 建的,不是围着扩展建的。
这份能力清单越往下读越触目惊心。mod 可以:
读取你的秘密:环境变量和设置文件,"包括你保存在其中的 API key"。
批准被你阻止的工具调用:批准工具调用的 mod 可以为"一个 ask rule 会提示的、或者你自己的 PreToolUse hook 阻止的"工具调用开绿灯。你的 hook 说了不,mod 说是,mod 赢了。
在你的审查日志之前重写事件:hooks 形成一条链,mod 可以观察、重写或完全回答任何事件。包括你的审计日志原本指望记录的那些。
所以当有人说 "mods 有沙箱隔离" 时,他们描述的是 JS 运行时。当 Anthropic 说 "mods 没有沙箱隔离" 时,他们描述的是你的文件、你的进程、你的网络和你的 API key。两者都是真的。重要的是第二个。
Anthropic 划定的唯一一道硬边界是这样的,而且很说明问题:
"mod 可以重新定义 Claude Code 界面的大部分样式,但不能碰权限提示框。它不能改变提示框显示的内容。"
mod 可以重绘几乎整个 UI、窗格、提示框上方的横条、toast 通知,但权限对话框始终是 Claude Code 自己的。无论 mod 绘制了什么,当出现"你确定吗?"的询问时,那个对话框不能被伪造、不能被重新定义样式、不能被替换内容。
想想为什么那条限制专门存在。如果 mod 能重新定义权限提示框的样式,它就可以向你显示"运行测试?",而实际上批准的是 rm -rf。Anthropic 加固了唯一一个你的眼睛是安全控制手段的 UI 元素,其余一切则完全开放。这精确地揭示了他们设计的威胁模型:mod 是拥有你权限的不可信代码,而权限提示框是房间里最后一个诚实的界面。
别再想象浏览器 iframe 了。正确的心智模型更古老、也更简单:
安装一个 mod,就是把 shell 交给别人。
不是受限的 shell,不是有人在旁监督的 shell。是你的 shell,带着你的环境变量、你的文件、你的网络,以及覆盖你为 Agent 搭建的防护栏的能力。JS 运行时沙箱是真实的工程实践,它防止模块互相踩踏,给引擎一个干净的拦截点——但它从来都不是 mod 与你的机器之间的安全边界。
一旦你有了这个模型,安全检查清单就自己写出来了:
安装前运行 claude plugin validate,而不是安装后。它列出 mod 处理的事件以及它要求 Claude Code 执行的操作——文件读取、网络请求——但不运行任何代码。像读手机 App 的权限屏幕一样读它。如果一个 context-bar mod 想要网络访问权限,问问为什么。
知道你的关闭开关。从 /plugin 的 Installed 标签页禁用单个插件。用 --safe-mode 启动会话以丢弃所有自定义。在 ~/.claude/settings.json 中设置 "disableAllHooks": true 作为总开关。组织可以使用 allowManagedModsOnly,在保持 skills、commands 和 MCP servers 正常工作的同时阻止用户安装的 mods 加载。
最难审查的是那些小的 mods。40 行代码的 UI mod 看起来很安全,因为它很小。但 40 行足够读取 ~/.claude/settings.json、窃取 API key,并批准覆盖其痕迹的工具调用。大小不是安全属性。
以上都不是说你不应该安装 mods。而是说,你应该像把笔记本交给同事一样安装它们:只交给信任的人,并且清楚在他们持有期间他们能访问什么。
所以我的问题是:你已安装的 mods,claude plugin validate 说了什么?而你是在安装之前读的,还是现在才读?