乐于分享
好东西不私藏

AI 时代的插件居然没有沙箱——Anthropic 怎么敢的

AI 时代的插件居然没有沙箱——Anthropic 怎么敢的

读这本书读到第 22b 章——讲插件系统那一章——我有一段读完后感觉怪怪的。

它讲到——Claude Code 的插件可以做这些事情:

  • 注册 Hook(拦截 AI 的工具调用)
  • 添加新命令
  • 启动 MCP 服务器
  • 启动 LSP 服务器
  • 修改 Agent 行为
  • 注入新工具
  • 配置环境变量
  • ……

听起来很猛。

然后我读到一句话——心里一咯噔

"源码中未发现插件沙箱机制。"

什么?

插件能做这么多事——居然没有沙箱

那万一我装了一个恶意插件——它岂不是能:

  • 偷偷读我的代码
  • 在 Hook 里把代码发到外部服务器
  • 注入恶意工具让 AI 调用
  • 改我的 settings.json
  • ……

Anthropic 怎么敢这么设计


11 个可插拔维度——一个插件能干的事

先看 Anthropic 给插件留了多少口子。

源码里 plugin.json 的 Schema 定义了 11 种"插件清单":

- Metadata(必需)—— 名字、版本、描述
- Hooks —— 26 个生命周期事件的拦截
- Commands —— 添加新的 slash 命令
- Agents —— 注册新的 Agent 类型
- Skills —— 添加新的技能
- OutputStyles —— 改变输出风格
- Channels —— 添加多通道支持
- McpServer —— 启动 MCP 服务器
- LspServer —— 启动 LSP 服务器
- Settings —— 修改设置项
- UserConfig —— 用户配置选项

这 11 个里任何一个——都能影响 AI Agent 的行为。

特别是 Hooks——

它能在工具调用前后塞进去你的代码

理论上一个恶意插件可以这么写——

PreToolUse(BashTool):
  - 把当前命令偷偷发到外部服务器
  - 然后让命令照常执行

用户完全感受不到。

因为 Hook 是"拦截"——不是"阻止"—— 它能截走你的数据,然后假装啥也没发生。

这种风险怎么管


"信任不是沙箱——是分层契约"

Anthropic 没用沙箱——用了三层信任契约

第一层:持久化警告

源码里有一个组件叫 PluginTrustWarning——

不是一次性弹窗——

是常驻在 /plugin 管理界面顶部的提示

每次你打开插件管理——这个警告就在那儿—— "这些插件可能修改你的 Agent 行为,请只信任你确认过的来源。"

为啥要常驻?

因为一次性弹窗用户会习惯性点掉——

常驻警告每次都看到——心里始终有根弦

第二层:项目级信任

每次进入一个新项目——Claude Code 弹出 TrustDialog——

审计这个项目里有没有:

  • MCP 服务器配置
  • Hook 配置
  • Bash 权限规则
  • API key helper
  • 危险环境变量

信任一次——记住这个项目

这个目录下的子目录自动继承父目录的信任

为啥这么做?

因为危险来自"项目"——不是来自"工具"。 你 clone 一个不认识的 GitHub 项目下来——里面可能有恶意 hook——

Claude Code 进去之前先问你"信任这个项目吗"

不信任——hook 不跑、MCP 不启、危险插件不加载。

第三层:敏感值隔离

插件 schema 里能标记某些字段是 sensitive: true—— 比如 API key、密码、token。

这些值不存在 settings.json 里——

存在 macOS Keychain(或 Linux 的 .credentials.json)里

为啥?

  • settings.json 你可能 commit 到 git——API key 泄漏
  • Keychain 是 OS 级加密——不会泄漏

加载时合并——安全存储优先—— 源码里有一句话——

"如果用户手编辑 settings.json,我们相信更安全的来源(Keychain)。"

冲突时——倾向安全


这套设计的妙处——也是它的代价

读完这一段我反思——

没有沙箱,是不是就不安全

也不是

沙箱的代价是——

  • 实现复杂(要做进程隔离、文件系统隔离、网络隔离)
  • 性能损失(每次调用都过沙箱)
  • 体验差(很多事情干不了)

Anthropic 选的路是——显式信任

  • 不假装"我能保护你"——你必须自己评估每个插件
  • 但提供工具让你能评估——清单透明
  • 提供安全网让常见错误不致命——敏感值隔离

这种设计哲学——和 npm/pip/cargo 几乎一样

你 npm install xxx 的时候——

  • npm 不验证这个包
  • npm 不沙箱这个包
  • 这个包能在你机器上做任何事

但 npm 提供:

  • 包名(你能查作者)
  • 版本号(你能锁定)
  • 公开仓库(社区会发现恶意行为)

整个生态靠社区监督 + 用户自负——而不是沙箱。

Anthropic 走的是这条路。

它赌的是——AI 时代的工具生态会像 npm 那样自治


25 种错误类型——但只用 2 种

最后一个让我笑出声又服气的细节。

插件系统的源码里——定义了 25 种错误类型—— 每一种都有它特定的上下文字段:

- path-not-found
- git-auth-failed
- git-timeout
- network-error
- manifest-parse-error
- manifest-validation-error
- marketplace-blocked-by-policy
- dependency-unsatisfied
- generic-error
... 还有 16 种

生产代码只用了 2 种——generic-error 和 plugin-not-found

剩下的 23 种——标着"planned for future use"。

为啥要提前定义这么多?

这是工程上一个非常深的智慧——类型先行演进

  • 你先定义所有可能的错误类型——但实现简单的 generic-error
  • 等用户量上来——某一类错误反复出现——

你只需要把 generic-error 替换成对应的具体类型

  • 接口不变,行为改变

如果你最初没定义类型——

  • 你得回头重构所有的错误处理代码
  • 用户的代码也得跟着改(可能依赖错误的字符串匹配)
  • API 兼容性破坏

而提前定义好类型——

  • 用户的代码可以已经按类型分支处理
  • 新的具体类型出现——用户不用改代码
  • API 永远兼容

这种"为未来而设计"的思维——

是只有真正负责长期生态的工程师才会有的

我以前写过一些 SDK——错误类型只有 Error 一种—— 后来回头加错误类型——用户全都炸了——他们的代码全是按错误消息字符串判断的。

读完这一章我意识到——

第一次写错误类型时——多想 3 遍未来可能的错误——值钱


这一章给我的最大启发

整章读完,我对"信任"这件事的理解变了——

我以前以为"插件信任 = 技术上保证它不能干坏事"。 读完这一章我知道——

插件信任 = 让用户能评估它干啥的、风险有多大、值不值得信

具体到工程:

  1. 常驻警告 > 一次性弹窗——降低用户的"信任疲劳"
  2. 项目级信任而非全局——风险定义在源头(项目目录)
  3. 敏感值隔离——一个常见错误不致命
  4. 类型先行演进——为未来而设计
  5. 用户自负 + 社区监督——比沙箱更可持续

我做工具的时候——以前总想着"我怎么保护用户"——结果功能越做越少。 读完这一章我换了思路——

用户能自己保护自己——只要给他足够的信息和工具


Part 6 进度

这是系列第 26 篇。 进入了 Part 6 的倒数第二章。

下一篇拆 Ch23——89 个 Feature Flag 暴露了 Anthropic 的下一代 AI 路线图