2-权限设计
规划了一个长任务,结果总是需要授予权限而不能脱身; 授予完全权限又害怕删盘跑路……
Anthropic 官方工程博客披露:用户实际批准了 93% 的权限提示。社区把这种状态命名为 permission fatigue(权限疲劳)——「几秒钟点一次批准,最后你根本不读就批了」,这本身就是安全风险。
「全权放手又怕删盘」 —— --dangerously-skip-permissions 模式清空了 home 目录、抹掉整台 Mac;删了生产数据库记录,这些都是真实的事故。
其实如今的 Claude Code 权限系统在两端之间提供多个档位,只要善于利用,就可以做到解脱于权限确认,也不怕事故出现,能够大大提升长任务的效率和安全性。
目录
1.1 一次工具调用的权限流程
权限规则由 Claude Code(harness层)强制执行,不是由模型决定。这是整个权限设计的根基。
一次工具调用(如 Bash("rm -rf /tmp/x"))从发起到执行,权限系统按以下顺序介入:
权限模式初始化(CLI 参数 / settings 加载)
│
▼
一次工具调用
│
▼
┌────────────────────────────────────┐
│ ① 规则评估 │ ← deny/ask 在所有模式生效
│ deny → ask → allow │
└─────────────────┬──────────────────┘
命中 │ │没命中任何规则
▼ ▼
deny → 阻止 按 mode 处置(六种):
ask → 询问 bypassPermissions → 断路器(rm -rf / ~)→ 其余放行
allow→ 放行 acceptEdits → 自动接受(受保护路径写入触发提示)
│ default → 向用户询问
│ plan → 禁写
│ auto → 分类器自主判断
│ dontAsk → 自动拒绝
│ │
└────────────────┬───────────────┘
▼
所有放行 / 批准
│
┌──────────────┴──────────────┐
非 Bash Bash
│ │
▼ ▼
┌──────────────────────┐ ┌────────────────────────────────────┐
│ 直接执行 │ │ ② 沙箱兜底(OS 级,仅 Bash) │
│ Read/Edit/Write/ │ └─────────────────┬──────────────────┘
│ Grep/Glob/WebFetch/ │ ▼
│ MCP/Task… │ 执行 / 阻止
└──────────────────────┘
- 受保护路径:写
.git/.claude等关键路径,acceptEdits 等放权模式也照样提示;bypass 放行。 - 规则 = 硬约束:deny/ask 永远生效,压过一切模式。这就是为什么
ask规则即使在bypassPermissions下也照样弹询问,deny更是任何模式下都会拦截。
权限与沙箱是两层互补防御:
- 权限——控制 Claude 能用哪些工具、访问哪些文件/域。作用于所有工具(Bash、Read、Edit、WebFetch、MCP 等)。
- 沙箱——OS 级强制,限制 Bash 工具的文件系统与网络访问。仅作用于 Bash 及其子进程。
1.2 权限管理都在 settings.json 里
Claude Code 的所有权限配置都写在 settings.json 里,权限相关的字段集中在顶层 permissions 对象下:
{
"permissions": {
"defaultMode": "acceptEdits", // 权限模式(见 1.3)
"allow": ["Bash(npm run test:*)"], // 规则三态(见 1.4)
"ask": ["Bash(git push *)"],
"deny": ["Read(./.env)"],
"additionalDirectories": ["../docs/"]
},
"sandbox": { "enabled": true } // 沙箱(见 1.6)
}
而且它不是单个文件,而是分层的一组:Claude Code 按固定层级合并——managed(企业强制、最高优先级、不可覆盖)→ 用户级 ~/.claude/settings.json → 项目级 <项目>/.claude/settings.json(进 git、团队共享)→ 本地级 .claude/settings.local.json(gitignore、个人覆盖)。
1.3 权限模式与设定途径
1.3.1 六种模式
根据官方文档,settings.json 中 permissions.defaultMode 字段决定 Claude Code 启动时的初始权限模式,共有六个可选值,沿自主性↔安全性分布如下:
自主性低(安全) 自主性高(放手)
dontAsk ── default ── plan ── acceptEdits ── auto ── bypassPermissions
| 模式 | 行为 |
|---|---|
dontAsk | 只放行 permissions.allow 规则集里的,未预批准的工具自动拒绝,连问都不问 |
default | 每个工具首次使用时询问 |
plan | 只读,不编辑源文件(写操作被禁,非询问) |
acceptEdits | 自动接受工作目录或 additionalDirectories目录中的文件编辑命令、 固定的文件系统命令(mkdir/touch/mv/cp/rm/rmdir/sed),其他 Bash 仍询问 |
auto | 分类器自动批准多数工具调用,后台安全检查验证动作符合请求(研究预览) |
bypassPermissions | ask 规则依旧询问,deny 规则直接阻断,断路器(rm -rf //rm -rf ~)最后一步拦截;其余全部放行,包括受保护路径 |
auto受账户计划/模型/提供商限制,第三方 API 未必可用,本文不展开。任意模式都压不住 deny / 显式 ask。
1.3.2 设定权限模式的三种途径
| 途径 | 字段/方式 | 作用域 |
|---|---|---|
settings.json | permissions.defaultMode | 每次启动的初始模式 |
| CLI 启动参数 | --permission-mode xxx | 单次会话覆盖 defaultMode |
Shift+Tab / Alt+M | 会话中按键 | 当次临时切换 |
字段值/cli参数: acceptEdits, auto, bypassPermissions, default, dontAsk, plan.
优先级:CLI 参数 > defaultMode(Shift+Tab 只在运行时临时改、不落盘)。
作用域限制:
- 项目/本地级写
"auto"被忽略(v2.1.142+,防仓库给自己授权)。 - 云会话里
"dontAsk"/"bypassPermissions"也被忽略。 "bypassPermissions":项目/本地级允许生效(本地),但强制走确认提示。
别名:--dangerously-skip-permissions ≡ --permission-mode bypassPermissions(同物旧名)。
Shift+Tab(运行时切换)——基础循环 default → acceptEdits → plan;auto/bypassPermissions 是可选模式,启用后插在 plan 之后。
1.4 规则三态与评估顺序
settings.json 的 permissions 对象:
{
"permissions": {
"allow": ["Bash(npm run test:*)"],
"ask": ["Bash(git push *)"],
"deny": ["Read(./.env)", "Bash(curl *)"]
}
}
三条评估铁律:
- 顺序:deny → ask → allow,首个匹配即定论,特异性不改变顺序。
- 匹配的
ask即使有更具体的allow同时匹配,仍询问。 - 裸工具名 deny(如
Bash)把工具从模型 context 完全移除——模型看不见它,无从调用;作用域 deny(如Bash(rm *))保留工具,但阻止匹配调用。
1.5 受保护路径
一小批「写它就提示」的关键路径,在除 bypassPermissions 外的所有模式下都拦,防 Agent 静默损坏仓库状态和 Claude 自身配置。安全检查发生在 allow 规则之前,所以连显式 Edit(.claude/**) 也压不住它。
重点三类(按防护目的):
- 仓库状态:
.git/.gitconfig/.gitmodules——防静默篡改历史、版本控制配置 - Claude 自身配置:
.claude/.mcp.json/.claude.json——防 Agent 给自己加 allow/bypass、改 MCP 配置 - Shell rc:
.bashrc/.zshrc/.profile等——防写 rc 做跨会话持久化 / 注入
其余还有一批配置文件:包管理器(.npmrc / .yarnrc…)、构建工具(.bazelrc / gradle-wrapper.properties…)、Git hooks(.pre-commit-config.yaml / lefthook.yml…)、IDE(.vscode / .idea)、.devcontainer、.ripgreprc / pyrightconfig.json 等,以及 .config/git / .husky / .cargo / .yarn / .mvn 等目录。
.claude/worktrees是例外——那是 Claude 自己的 git worktree 存储,不在保护范围内。
各模式处置:
| 模式 | 受保护路径写入 |
|---|---|
default / acceptEdits / plan | 提示 |
auto | 交分类器判定 |
dontAsk | 直接拒绝 |
bypassPermissions | 放行(v2.1.126 起) |
1.6 实用组合:两套拿来就能用的搭配
1.6.1 安全放手编辑:acceptEdits
acceptEdits 听着像「大撒手」,实际自主性并不高——默认放行的只有一小撮:
默认通过(不用确认):
- 文件编辑:
Edit/Write/NotebookEdit三个工具,路径在工作目录或additionalDirectories内 - 7 个 Bash 文件系统命令:
mkdir/touch/mv/cp/rm/rmdir/sed(可带LANG=C等环境变量前缀、timeout/nice/nohup包装器)
仍需确认(高频但不在上面名单里):
Skill调用、MCP 工具调用——都属「需权限」工具,acceptEdits 不覆盖- 构建 / 测试 / 装包:
npm install、npm test、go build… 既不在只读集、也不在 7 命令名单 - 写操作 git:
git commit、git push… WebFetch首次访问新域名
注意区分:
ls/cat/grep/cd/ 只读 git(status/log)这些在所有模式(含default)下本就不询问——那是「只读命令集」的功劳,不算 acceptEdits 放的权。
所以 acceptEdits 真正省下的,主要是编辑文件和那 7 个文件系统命令的反复确认;其余高频操作仍要逐个批。配合受保护路径(见 1.5)锁住危险文件,编辑类长任务可安全放手。
{ "permissions": { "defaultMode": "acceptEdits" } }
claude --permission-mode acceptEdits
1.6.2 受控的危险模式:bypass + deny
bypass 跳过所有提示和安全检查(含受保护路径,自 v2.1.126 起也放行),几乎一切立即执行。仍能拦住的只剩三样:deny 规则直接阻断、显式 ask 规则仍提示、断路器(rm -rf /、rm -rf ~ 针对根/home 本身的删除仍提示,防模型手滑)。
实战套路:在隔离环境(容器/VM)里开 bypass,再用 deny 锁死绝不允许的、ask 拦下要审一下的:
{
"permissions": {
"deny": [
"Read(~/.ssh/**)", "Read(~/.aws/**)", "Read(~/.gnupg/**)",
"Read(~/.kube/config)", "Read(~/.docker/config.json)", "Read(~/.npmrc)",
"Read(.env)", "Read(**/.env.*)", "Read(**/*.pem)", "Read(**/*.key)", "Read(**/id_rsa*)",
"Bash(git push --force*)", "Bash(git push -f *)", "Bash(git push -f)",
"Bash(git push * --force*)", "Bash(git push * -f *)",
"Bash(git push --delete *)", "Bash(git push --mirror*)",
"Bash(rm -rf *)", "Bash(rm -r *)", "Bash(rm -fr *)",
"Bash(rm -rf .)", "Bash(rm -rf ./*)",
"Bash(find * -delete)", "Bash(find * -exec rm*)",
"Bash(mkfs*)", "Bash(dd *)", "Bash(shred*)",
"Bash(psql * DROP*)", "Bash(psql * drop*)",
"Bash(mysql * DROP*)", "Bash(mysql * drop*)",
"Bash(redis-cli *flush*)",
"Bash(sudo *)", "Bash(su *)",
"Edit(~/.bashrc)", "Edit(~/.zshrc)", "Edit(~/.profile)"
],
"ask": [
"Bash(curl *)", "Bash(wget *)", "Bash(nc *)", "Bash(netcat *)",
"Bash(rm *)",
"Bash(npm install *)", "Bash(npm i *)", "Bash(pip install *)",
"Bash(pip3 install *)", "Bash(yarn add *)", "Bash(pnpm add *)",
"Edit(.claude/**)", "Edit(**/.mcp.json)", "Edit(**/.claude.json)", "Edit(~/.claude/**)",
"Edit(**/.gitconfig)", "Edit(~/.gitconfig)"
]
}
}
启动该模式:
claude --permission-mode bypassPermissions
# 等价别名(旧名,同物)
claude --dangerously-skip-permissions
1.7 沙箱:OS 级兜底
Claude Code 放行 Bash 的执行权限后,OS 层还会再拦一道。
沙箱是什么——Claude 跑 Bash 命令时,OS 在这个进程外面套一层"笼子":它碰到的每一次读文件、写文件、联网,OS 都拿你设的边界过一遍,越界直接拦掉,跟 Claude 想不想跑无关。macOS 用内置 Seatbelt,Linux/WSL2 用 bubblewrap,Windows不支持。
一个拦截例子——Claude 跑 npm install some-package 被批准,但这包的安装脚本里藏了"读 ~/.ssh/id_rsa → POST 到 evil.com"的逻辑
- 没沙箱:
npm用你本人的权限在跑,能读你所有文件、能随便联网,脚本读出私钥发走,私钥泄露; - 有沙箱:命令照样跑,但跑在笼子里——读
~/.ssh/id_rsa被 OS 拦(denyRead)、连evil.com被拦(不在allowedDomains),脚本死掉,私钥没事。
这就是"兜底"的真意:就算提示注入骗过模型决策、就算权限放行了,OS 这层照样按物理边界拦。
字段就是笼子的"一面墙"或"一个开关":
{
"sandbox": {
// 总闸
"enabled": true,
// 关键:笼子既然兜得住,就别逐条弹 Bash 提示了——
// 心智从"每条审批"切到"只管笼子边界"。
// settings.json中的 ask(如 ["Bash(git push *)"])、显式 deny;
// 以及针对 / 和 home 目录的 rm 命令仍会拦截并提示。
"autoAllowBashIfSandboxed": true,
// 必须放出笼子跑:docker、或 gh/gcloud/terraform 这类
// Go 系 CLI(macOS Seatbelt 里 TLS 验证会挂)
"excludedCommands": ["docker *"],
// 是否允许 Claude 用 dangerouslyDisableSandbox 紧急跳出
// 重试;企业要"绝不脱笼"设 false
"allowUnsandboxedCommands": true,
"filesystem": {
// 不准读:私钥、整个 .aws 目录
"denyRead": ["~/.ssh/**", "~/.aws"],
// 不准写
"denyWrite": ["/etc", "/usr/local/bin"],
// 在 ~/.aws 禁区里再开一个小口读 config(压过 denyRead)
"allowRead": ["~/.aws/config"],
// 默认工作目录之外,额外允许写的路径
"allowWrite": ["/tmp/build", "~/.kube"]
},
"network": {
// 白名单:只放行装包要用的,其余默认拦
"allowedDomains": ["github.com", "*.npmjs.org"],
// 黑名单命中优先于 allowed(deny 胜)
"deniedDomains": ["*.evil.com"]
}
}
}
要点:
- 沙箱
filesystem的 allow 可以在 deny 里开小口(特指allowRead/allowWrite):allowRead优先于denyRead,allowWrite对denyWrite同理。典型套路:denyWrite整个 home、再allowWrite: ["."]只开工作目录。 - 权限规则的 deny 路径会自动灌进沙箱:在
permissions.deny里写一条Read(./.env)(见 1.4),它既 挡住 Claude 自己的 Read 工具,路径.env又被并入沙箱denyRead——一条规则两层生效。反向不成立:单写在sandbox.filesystem.denyRead里的路径只拦子进程,挡不了 Claude 自己的 Read。 network的黑名单级别最高:deniedDomains压过allowedDomains。⚠️allowUnixSockets慎用——放行/var/run/docker.sock等于把宿主机交给容器。
参考来源(官方文档,截至 2026-06)
- Configure permissions —— 规则语法、模式、managed 策略(最权威,细节最全)
- Choose a permission mode —— 六种模式、保护路径完整清单、auto/dontAsk/bypass 细节
- Configure the sandboxed Bash tool —— 沙箱配置与 OS 兜底
- Claude Code settings ——
settings.json字段全表 - Security —— 注入防护、权限安全边界
- Interactive mode ——
Shift+Tab等快捷键 - CLI reference ——
--permission-mode等启动参数 - Auto mode for Claude Code —— research preview 公告(93% 统计出处)
版本锚点:受保护路径在 bypass 下放行自 v2.1.126 起;
defaultMode: "auto"在项目/本地 settings 被忽略自 v2.1.142 起;Bedrock/Vertex/Foundry 的CLAUDE_CODE_ENABLE_AUTO_MODE自 v2.1.158 起。