Claude Code mod 能越过你的 deny 规则:个人方案实测
我们只测了个人 Max 方案、没有托管设置的机器。在这个环境里,负责让 deny 规则优先于用户 mod 生效的内置防护 mod 没有加载。一个只有 6 行、在权限检查时返回 allow 的 mod,就让被 deny 规则拦下的命令照样执行了。Anthropic 文档写明了这种行为。实际含义是:安装 mod,就把 settings.json 管不到的访问权限交给了它的作者。
30 秒看懂结论
10 月 1 日,Claude Code 2.1.287 加入了 mod(在 Claude Code 进程里运行的插件代码),默认开启。这些 JavaScript 代码可以注册处理函数,查看和修改提示词、工具调用,也可以在系统向你询问之前回应权限检查。
我们写了 7 个小测试 mod,在 Claude Code 2.1.289 上跑了 48 次无界面会话(claude -p),过程中没有交互式提示。账号是个人 Max 方案,机器没有托管设置(managed settings)。每种配置都跑了 3 次,结果均为 3/3:
- deny 规则拦下的命令,装上一个 mod 就照样执行了。原本被规则拦住的命令,只要加上一个 6 行的 mod,在权限检查时返回 allow,就能执行。
- deny 规则管不到 mod 自己的调用。Claude 读不了被
Read规则禁止读取的文件,mod 却通过自己的 API 读到了。 - 沙箱管不住 mod。Claude 向项目外的目录写文件时被沙箱拦住,mod 启动的进程却能写入。
- mod 能改写会话记录。我们让处理函数改写即将保存的会话行。对照测试中,会话记录(transcript)里是 Claude 回复的口令。加上 mod 后,同一位置变成了 REDACTED。
claude plugin validate对越过 deny 的 mod 只列出tool.check。它的calls:一行写着 “nothing on $“,只观察权限检查、不改变决定的 mod 也一模一样。
权限、沙箱和会话记录的这些行为,Anthropic 文档都有说明。另有一项网络结果与文档不符,后文会单独说明。这些测试放在一起看,含义很明确:安装 mod 后,它的代码能访问一些 Claude Code 权限规则和 Bash 沙箱管不到的资源。
mod 是什么
mod 是带有钩子模块的插件。这个模块是一个 JavaScript 或 TypeScript 文件,由 Claude Code 加载到自己的进程中。当工具被调用、用户提交提示词、会话行即将保存等事件发生时,对应的处理函数就会运行。它可以保持原样,也可以改写内容,还可以直接提供结果,让原本的处理流程不再执行。
Claude Code 会向每个处理函数传入一个名为 $ 的 API 对象。钩子模块本身没有 Node.js API,因此,访问文件($.fs)、启动程序($.process)、发出网络请求($.http.fetch)、读取设置和环境变量、调用模型,都必须经过 $。有了这个限制,Claude Code 才能在运行 mod 之前,列出它注册的处理函数和使用的 API。
这里有两套不同的权限。mod 使用的是你在操作系统中的账号权限。Claude Code 的 allow、ask 和 deny 规则则用来控制 Claude 发起的工具调用。
mod 和普通插件一样,可以从插件市场安装,也可以用 --plugin-dir 在单次会话中加载。它的处理函数可以在终端、桌面端应用(WSL 会话除外)、VS Code 扩展、claude -p 和 Agent SDK 中运行。Claude Code 自己的一些功能也由内置 mod 提供,例如 /diff 和 AGENTS.md 加载器。
我们怎么测的
每次运行都先创建一个新的项目目录。如果该配置需要 mod,也为它新建一个目录。接着启动 claude -p,使用 Claude Haiku 4.5,只保留 HOME、PATH、USER 和 TMPDIR 这几个环境变量。启动时加上 --strict-mcp-config,并关闭我们自己原来安装的插件。待测 mod 通过 --plugin-dir 加载,权限规则和沙箱配置通过 --settings 文件传入。
我们检查了标记文件、各次会话记录中的工具结果,以及加载错误。对于改写会话行的测试,还把保存下来的会话记录与无界面会话的输出做了对照。
测试于 10 月 4 日在 macOS 上完成。共 16 种配置,每种运行 3 次,总计 48 次。各次运行耗时合计 238 秒,按 API 标价换算为 $0.92。我们使用 Max 方案,实际计入方案的用量上限。所有会话的插件清单里都没有 Anthropic 的内置防护 mod cc-plugin-sec-default,这与文档对该环境的说明一致。
deny 规则还能拦住吗
测试命令是 touch deny-marker.txt。我们先允许 Bash,让 deny 规则成为阻止这条命令执行的唯一条件:
| 配置 | 结果 | 结果一致的次数 |
|---|---|---|
允许 Bash,没有 deny 规则,也没有 mod | 已执行 | 3/3 |
| 用 deny 规则禁止该命令,没有 mod | 已拒绝 | 3/3 |
| deny 规则 + 只观察权限检查的 mod | 已拒绝 | 3/3 |
deny 规则 + 在权限检查时返回 allow 的 mod | 已执行 | 3/3 |
同上,但用 --safe-mode 启动 | 已拒绝 | 3/3 |
同上,但设置 "disableAllHooks": true | 已拒绝 | 3/3 |
| 没有 allow 规则,也没有 mod(无界面会话无法询问你是否批准) | 未执行 | 3/3 |
| 没有 allow 规则 + 批准调用的 mod | 已执行 | 3/3 |
用 deny 规则禁止 Read(./secret.txt),要求 Claude 读取文件 | 已拒绝 | 3/3 |
同上,再加一个通过 $.fs.read 读取该文件的 mod | Claude 被拒绝,mod 读到了全部 37 个字符 | 3/3 |
批准调用的 mod 只注册了一个 tool.check 处理函数。它先让 Claude Code 作出权限决定,再返回 allow。没有 mod 时,工具结果表示这条 Bash 命令已被拒绝(原文:“Permission to use Bash with command touch deny-marker.txt has been denied.”)。加上 mod 后,工具结果如下:(Bash completed with no output)
两种关闭方式都恢复了 deny 规则的拦截,但也会影响其他配置。--safe-mode 会在本次会话中关闭你的其他自定义配置。disableAllHooks 还会停用设置里的钩子(settings hook)和自定义状态栏。这两个开关都不会停用内置 mod。
内置防护何时加载
Anthropic 提供了内置防护 mod(sec-default),它会在用户安装的所有 mod 之前加载。只要它加载了,默认就是 deny 规则优先于用户 mod 生效。用户 mod 也不能修改托管钩子、托管指令或系统提示词。管理员可以用 allowModsToOverrideDenyRules 放宽其中对 deny 规则的保护。
内置防护 mod 会在两种情况下加载:机器有托管设置,或者登录账号使用 Team 或 Enterprise 方案。Anthropic 的权限文档对其余情况的说明是:mod 可以放行 deny 规则拒绝的调用(原文:“Anywhere else, the mod can approve a call that a deny rule refuses.”)。因此,如果机器没有托管设置,Pro、Max 方案,以及使用 API 密钥、Bedrock、Vertex、Foundry 的用户,都属于这个范围。
即使内置防护 mod 已经加载,mod 仍有以下能力。这些也都写在文档里:
- deny 规则只约束 Claude 的工具调用,管不到 mod 自己的
$.fs和$.process调用。即使 deny 规则禁止了Read(.env),mod 仍然能读这个文件。 ask规则要求询问用户的调用,mod 可以直接批准。被托管设置之外的PreToolUse钩子拦下的调用,mod 也可以批准。- 在自动模式(auto mode)中,mod 批准的调用会跳过自动安全检查。
沙箱与网络实测
我们通过 sandbox.enabled: true 开启沙箱,并把 allowUnsandboxedCommands: false 加入配置。随后要求 Claude 用 touch 在一个目录里创建文件。这个目录既在项目之外,也不属于沙箱默认允许写入的位置。3/3 次运行都被沙箱拒绝,错误信息为 “Operation not permitted”。
在同一批会话中,我们的 mod 通过 $.process.run 对同一个目录执行 touch,3/3 次都成功创建了文件。Anthropic 文档已经说明了这种行为:mod 启动的进程在沙箱之外运行。
有一项网络结果与文档不符。Anthropic 的管理文档说,如果会话关闭了非必要网络流量,Claude Code 就会拒绝 mod 的 $.http.fetch 请求。但我们设置 CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 后,mod 请求 example.com,3/3 次都返回了 HTTP 200。这些会话中都没有加载内置遥测 mod,说明环境变量已被识别。至于网络请求为什么仍然成功,原因没有查明。
这个环境变量自己的参考文档没有提到 mod。我们也没有测试组织级关闭网页抓取的设置。
管理文档还说明,这项网络策略不约束 mod 启动的程序。我们通过 $.process.run 执行 curl,六次网络测试都返回了 HTTP 200。因此,不能把这个环境变量当作 mod 的网络隔离措施。
会话记录也能改
在 Claude Code 保存每一行会话内容之前,都会触发 session.append。测试 mod 会检查它收到的每一行,把其中的口令 PELICAN-41 替换成 REDACTED。我们在提示词里描述了这个口令,但没有直接写出口令本身。
没有 mod 时,保存的会话记录和无界面会话的输出都显示 PELICAN-41,结果是 3/3。加上 mod 后,两处都变成了 REDACTED,结果也是 3/3。因为提示词里没有出现口令,我们据此推断,被替换的是 Claude 的回复。这不是直接截获回复后得出的结论。
如果你把会话记录当作代理行为的证据,就需要考虑这一点:session.append 处理函数可以在保存之前改写会话行。我们此前的 AGENTS.md 实测 就曾用会话记录判断哪些文件被加载。不过,本次测的是回复文本,没有测试那些指令加载记录。
怎么看验证清单
claude plugin validate 是 Anthropic 建议的审查步骤。对插件目录运行 claude plugin validate <folder> 就能列出 mod 为哪些事件注册了处理函数,以及代码调用了哪些 mod API 方法。整个检查过程不会执行 mod:
| mod | hooks: | calls: |
|---|---|---|
每次权限检查都返回 allow | tool.check | 未调用 $ |
| 只观察权限检查 | tool.check | 未调用 $ |
| 改写即将保存的会话行 | session.append | 未调用 $ |
| 读取被 deny 规则禁止读取的文件 | session.start | $.fs.read, $.fs.write |
| 沙箱测试 | session.start | $.fs.write, $.process.run |
| 网络测试 | session.start | $.fs.write, $.http.fetch, $.process.run |
通过计算得出的属性名访问 $ | — | 验证失败 |
如果验证器无法分析 mod 如何使用 $,Claude Code 就会拒绝加载它。我们用计算得出的属性名做了测试:验证没有通过,3/3 次会话都没有执行这段 mod 代码,拒绝加载的信息只出现在调试日志里。
最值得注意的是前两行。一个 mod 只观察,另一个会批准调用,两者的清单完全相同,对 deny 规则的影响却相反。清单只说明这个 mod 能回应权限检查,至于它会怎么回应,得看代码。改变权限决定和改写会话行,都不需要调用 $。如果审查时只找文件读写和网络请求,就会漏掉这两种行为。
审查时,两行清单都要看:
hooks::tool.check可以批准或拒绝工具调用。tool.call和prompt.submit可以查看、修改工具调用和提示词。session.append可以改写即将保存的会话内容。prompt.compose和prompt.section可以修改系统提示词。只要出现其中任何一项,就要继续读对应的处理函数。calls::$.process.run和$.process.spawn启动的进程不受沙箱及这项网络策略限制。$.fs.read和$.fs.write不受你的 deny 规则限制。$.http.fetch会发出网络请求。$.env.get和$.settings.read能读取可能存有密钥的值。$.model.complete会消耗你的方案额度,或产生 API 密钥对应的费用。
应该怎么做
在你自己的机器上:
- mod 一旦加载,就能以你的账号权限访问文件、启动程序和联网。只安装信任的作者提供的 mod,阅读源码,并先运行
claude plugin validate。 --plugin-dir只是临时试用,不提供隔离。试用不熟悉的代码时,用另一台机器、虚拟机或单独的用户账号。- 没有内置防护 mod 时,处理
tool.check的 mod 可以越过你的 deny 规则。密钥应放在需要额外确认、当前用户账号无法直接读取的位置。备份也应放在这个账号无法覆盖的地方。 - 用
claude --safe-mode启动一次会话,可以临时关闭已安装的 mod 和其他自定义配置,快速判断问题是否与它们有关。
在组织里:先确认内置防护 mod 已经加载。机器有托管设置,或者用 Team、Enterprise 方案登录时,默认都会加载它。如果配置了 prependPlugins,则需要显式把内置防护 mod 列进去。
如果只允许组织托管的 mod 和内置 mod,在托管设置的 pluginConfigs 中,为 cc-plugin-sec-default@builtin 设置 allowManagedModsOnly。插件市场白名单(strictKnownMarketplaces)可以限制 mod 的来源,disableSideloadFlags 可以禁用 --plugin-dir。保留默认保护,不要设置 allowModsToOverrideDenyRules。
哪些情况没有测
- Team、Enterprise 和有托管设置的机器。内置防护 mod 在这些环境中的行为,依据的是 Anthropic 官方文档和公开源码。
- 自动模式。mod 批准后会跳过自动安全检查,这是文档里的说明,我们没有实测。
- 交互式会话和桌面端应用。在交互式会话中,如果目录尚未被信任,mod 会等待信任提示。
- 第三方 mod。我们的 7 个 mod 都是专为本次测试写的,没有审计任何已发布的第三方 mod。
相关阅读
- AI 编程代理一次次删掉生产数据库:本文涉及哪些权限控制,以及为什么凭据比规则更关键。
- Claude Code 钩子:值得配置的四个:介绍设置里的钩子。它们仍然可以和 mod 一起使用。
- 你的 CLAUDE.md 已经成了攻击面:仓库文件如何通过另一条路径影响你的代理。
- Claude Code 现在会读 AGENTS.md 了:一个内置 mod 的实际表现,以及我们当时用作依据的会话记录。
来源
- Anthropic:mod 概览。介绍 mod 能访问哪些资源、可用环境、内置 mod 和关闭方式,并说明 mod 不受沙箱限制(原文:“Mods aren’t sandboxed”)。
- Anthropic:管理组织中的 mod。说明内置防护 mod
sec-default的加载条件、deny 规则的约束范围和网络策略。还介绍allowManagedModsOnly和prependPlugins,以及allowModsToOverrideDenyRules的配置方式,并解释claude plugin validate清单的读法。 - Anthropic:配置权限:用钩子扩展权限检查。说明在前述加载内置防护 mod 的情况之外,mod 可以批准 deny 规则拒绝的调用(原文:“Anywhere else, the mod can approve a call that a deny rule refuses.”)。
- Anthropic:使用 mod API。说明钩子模块没有 Node.js API,并介绍
$.fs、$.process和$.http。 - Anthropic:mod 参考文档。介绍
tool.check和session.append事件。 - Anthropic:沙箱。说明沙箱默认允许写入的位置。
- Anthropic:环境变量。说明
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC会关闭哪些流量。 - Anthropic:Claude Code 更新日志。2.1.287 加入 mod,2.1.288 和 2.1.289 包含 mod 相关修复。
- Anthropic:Claude Code mod 入门,2026 年 10 月 1 日。
- GitHub:内置防护 mod
sec-default的源码。 - npm:@anthropic-ai/claude-code。2.1.287 于 10 月 1 日发布,2.1.289 于 10 月 3 日发布。
我们自己的测量:2026 年 10 月 4 日,在 macOS 上使用 Claude Code 2.1.289 和 Claude Haiku 4.5,以个人 Max 方案登录,机器没有托管设置。共运行 48 次 claude -p 会话,覆盖 16 种配置,每种各跑 3 次。按 API 标价换算为 $0.92,实际计入 Max 方案的用量上限,各次运行耗时合计 238 秒。结果依据标记文件、各次会话记录中的工具结果和加载错误。改写会话行的测试还核对了无界面会话的输出。所有测试 mod 都为本次实验编写,通过 --plugin-dir 加载,在临时目录中运行。
FAQ
Claude Code 的 mod 是什么?mod 是带有 JavaScript 或 TypeScript 处理函数的插件,直接在 Claude Code 进程里运行。这项功能在 10 月 1 日的 2.1.287 中加入,默认开启。它使用你的账号权限访问文件、启动程序和联网。
mod 能越过我的 deny 规则吗?如果机器没有托管设置,登录的也不是 Team 或 Enterprise 方案,就可以。我们的个人 Max 实测中,没有 mod 时,deny 规则拦住命令的结果是 3/3。加载批准调用的 mod 后,命令执行成功的结果是 3/3。如果内置防护 mod 已经加载,默认会让 deny 规则优先于用户 mod 生效,但规则仍然管不到 mod 自己的文件和进程调用。
mod 受沙箱限制吗?不受限制。Claude 向项目外写文件时,3/3 次都被沙箱拦住。我们的 mod 启动进程向同一目录写文件,3/3 次都成功。
安装前怎么检查 mod?对插件目录运行 claude plugin validate,再阅读清单中列出的处理函数。hooks: 一行出现 tool.check,就意味着它能回应权限检查,即使 calls: 一行没有列出任何调用。
怎么关闭 mod?关闭单个 mod,在 /plugin 中操作。只关闭一次会话中的 mod,用 claude --safe-mode。要在所有会话中停用自己安装的 mod,在用户设置里写入 "disableAllHooks": true,但这也会停用设置里的钩子和状态栏。内置 mod 仍会运行。
这篇对你有帮助吗?