AI Native 手册
护栏与安全

权限规则:deny、ask、allow

用 settings.json 把“绝对不行”和“必须确认”写进系统,而不是写进提示词

亲历小工具产品组织阶段 · 通用更新于 2026-10-16

把“绝对不行”写进 deny,“必须我点头”写进 ask,只读和本地开发的命令写进 allow。这些规则由 Claude Code 执行,不靠模型自觉:CLAUDE.md 里写“不要 force push”只是建议,写进 deny 才是规则。

规则怎么生效

  • 顺序固定:先 deny,再 ask,最后 allow。 第一个命中的说了算,和规则写得多具体无关:宽泛的 deny 会盖过具体的 allow,ask 也会盖过 allow
  • 多个文件的规则会合并,不是覆盖。 任何一个文件里的 deny,别的文件里的 allow 都放不开
  • 写法是前缀加通配。 Bash(git push --force *) 里,第一个 * 之前的部分按原样匹配;末尾的“空格 + *”也能匹配不带参数的命令本身,所以 Bash(wrangler deploy *) 连光秃秃的 wrangler deploy 也会拦住
  • 复合命令拆开匹配。 cd app && rm -rf build 里任何一段命中 deny 或 ask,整条命令都会被拦下

随时可以用 /permissions 查看所有规则,它会标出每条规则来自哪个文件。细节见官方文档。

我实际用的规则

{
  "permissions": {
    "deny": [
      "Bash(rm -rf /)",
      "Bash(rm -rf ~)",
      "Bash(sudo rm -rf *)",
      "Bash(git push --force origin main *)",
      "Bash(git reset --hard origin/main *)"
    ],
    "ask": [
      "Bash(gh api * -X DELETE *)",
      "Bash(gh repo delete *)",
      "Bash(gh pr merge *)",
      "Bash(make db-reset *)",
      "Bash(docker volume rm *)",
      "Bash(docker compose down -v *)",
      "Bash(psql * -c DROP*)",
      "Bash(wrangler deploy *)",
      "Bash(vercel --prod *)",
      "Bash(wrangler d1 execute * --remote *)"
    ],
    "allow": [
      "Bash(wrangler whoami)",
      "Bash(vercel ls *)",
      "Bash(gh pr view *)",
      "Bash(gh run list *)"
    ]
  }
}

三组规则的逻辑:

  • deny:不可逆,而且 agent 永远没有正当理由去做的事:删根目录和家目录、force push 到 main、把 main 硬重置成远端的样子
  • ask:正当但影响大的事:通过 API 删除资源、删仓库、合并 PR、重置或删除数据库、删 Docker 卷、部署生产、往远端数据库写数据。这些我要亲自点头
  • allow:只读和本地开发的命令。放行它们是为了少弹窗。弹窗少了,剩下那几个你才会认真看

想从历史记录里自动整理出常用只读命令的放行列表,可以用内置的 /fewer-permission-prompts。

这些规则拦不住什么

Bash 规则匹配的是命令文本,官方文档明确说它不是程序级的安全边界:Bash(rm *) 拦得住 rm -rf build/,拦不住 /bin/rm -rf build/ 和 bash -c 'rm -rf build/';Bash(git push *) 拦不住 git -C . push。

我自己的规则里就有写得不够好的。按官方文档的匹配规则推算:

  • Bash(gh api * -X DELETE *) 有两个通配,要求 -X DELETE 前后都还有内容。最常见的 gh api -X DELETE repos/o/r 和 gh api repos/o/r -X DELETE 都匹配不上,--method DELETE 也不行。改成 Bash(gh api *DELETE*) 更稳,误报的代价只是多问一次
  • Bash(psql * -c DROP*) 要求 -c 后面紧跟大写的 DROP。真实命令几乎都带引号(-c "DROP TABLE ..."),写成小写也很常见,很可能匹配不上
  • ask 里只写了 Bash(wrangler deploy *),没写 Bash(bunx wrangler deploy *)。规则按命令文本匹配,不会自动去掉 bunx 这类前缀,用 bunx 部署时这条拦不住,两种写法都要列(见小工具上线)

所以权限规则只能拦住 agent 通常的写法。需要正则、不区分大小写这类更强的匹配,交给 hook;需要真正的隔离,用官方的沙箱(/sandbox)。

放在哪个文件

文件适合放什么
~/.claude/settings.json(用户级)对所有项目都成立的 deny:删家目录、force push 到 main
.claude/settings.json(项目级,进 git)这个仓库特有的 ask:部署、数据库命令,团队共享
.claude/settings.local.json(项目级,不进 git)只对你本机成立的 allow
托管设置最关键的几条 deny,普通工具改不到

用户级文件有个弱点:配置切换工具会整份重写的,正是这个文件(见切换工具会整份覆盖设置)。

关键规则放托管设置

试用中 托管设置本来是企业管理员下发策略用的,优先级最高,用户级、项目级设置和命令行参数都覆盖不了它。一个人用也可以借这个机制:把最关键的几条 deny 放进去。写这个目录需要管理员权限,平时用的工具碰不到它。这是我在三层护栏全部失效之后的补法,还在试。

系统路径
macOS/Library/Application Support/ClaudeCode/managed-settings.json
Linux、WSL/etc/claude-code/managed-settings.json
WindowsC:\Program Files\ClaudeCode\managed-settings.json

动手之前先确认这个文件存不存在。已经存在、而且不是你写的(公司电脑、管理员下发的策略),就交给管理员,不要自己改,改了也可能被下一次下发覆盖。下面以 macOS 为例。

文件不存在时才新建。这段命令自带检查,文件已经存在就什么都不改,只把内容打印出来:

f="/Library/Application Support/ClaudeCode/managed-settings.json"
if [ -e "$f" ]; then
  echo "托管设置已经存在,没有改动。先看看内容:" && sudo cat "$f"
else
  sudo mkdir -p "$(dirname "$f")"
  sudo tee "$f" > /dev/null <<'EOF'
{
  "permissions": {
    "deny": [
      "Bash(rm -rf /)",
      "Bash(rm -rf ~)",
      "Bash(git push --force origin main *)",
      "Bash(git reset --hard origin/main *)"
    ]
  }
}
EOF
fi

文件已经存在、而且是你自己以前写的:先备份,再把新规则合并进去,不要整份覆盖:

f="/Library/Application Support/ClaudeCode/managed-settings.json"
sudo cp "$f" "$f.bak-$(date +%Y%m%d-%H%M%S)"
tmp="$(mktemp)"
sudo cat "$f" | jq --argjson add '["Bash(rm -rf /)","Bash(rm -rf ~)","Bash(git push --force origin main *)","Bash(git reset --hard origin/main *)"]' \
  '.permissions.deny = (((.permissions.deny // []) + $add) | unique)' > "$tmp" \
  && sudo cp "$tmp" "$f"
rm -f "$tmp"
sudo cat "$f" | jq empty && echo "JSON 有效"

最后一行别省:托管设置不是合法的 JSON 时,Claude Code 会直接拒绝启动。然后新开一个会话,运行 /status:Setting sources 一行出现 Enterprise managed settings (file),就说明生效了(见官方文档)。

两点提醒:托管设置对这台机器上的所有项目都生效;本机管理员仍然能改它,所以它防的是工具,不是你自己。另外,不要在个人电脑上设 allowManagedPermissionRulesOnly,它会让你在其他文件里写的权限规则全部失效。

bypass 模式下,ask 规则靠得住吗

试用中 这一条请你自己验证,不要直接相信我或任何一篇文章。

官方文档(截至 2026-10)的说法是:deny 规则在所有模式下都生效,包括 bypassPermissions;命中 ask 规则的操作,在任何模式下都不会被自动放行,也包括 bypassPermissions(原文)。但 bypass 模式的实际行为在过去一年里随版本变过不止一次,官方仓库里有一长串相关 issue。如果你和我一样习惯开着 bypass 图省事,就别把关键防线押在 ask 上。

用一条无害的规则,测一下你当前的版本和配置:

在 .claude/settings.local.json 里加一条测试规则(已经有 permissions 的话合并进去):

{
  "permissions": {
    "ask": ["Bash(echo ask-check *)"]
  }
}

用你平时的模式启动 Claude Code,让它运行 echo ask-check hello。

弹出确认,说明 ask 在你的环境里有效;直接执行了,说明不能依赖它。测完删掉这条规则。

不管结果如何,分工都一样:不可逆的放 deny,需要兜底的放 hook。官方文档写明,PreToolUse hook 返回 deny 时,即使在 bypass 模式下也会拦下。想少弹窗又不想完全放开,官方更推荐 auto 模式:由一个分类器在后台审查每个动作。

硬规则下沉

判断标准:能机械验证的进系统,需要判断的留在提示词里。

规则放哪
某条命令绝对不能执行deny
某类操作必须我确认ask
执行之前必须先做某件事,比如备份hook
某些文件不能读、不能改deny 里加 Read(./.env)、Edit(./secrets/**) 这类规则
需要判断的事:什么算 trivial 修复、什么时候必须先写 specCLAUDE.md

常见坑

相关

本页目录