# settings.json 权限白名单示例

> 一份项目级 .claude/settings.json 示例，用 allow、ask、deny 三类规则减少权限弹窗，同时挡住密钥文件读取，并解释规则顺序与路径写法的坑。

## 适用场景

每次让 Claude 跑 `npm run test` 都要点一次确认，批准到第十次时已经只是在机械点击。更合理的做法是把确定安全的命令预先放行，把有风险的保持询问，把绝对不该碰的直接拒绝。

下面这份配置面向一个典型的 Node 项目。你可以改成自己的命令，思路通用：读和检查类放行，改变远端或依赖的询问，密钥类拒绝。

## 模板

保存为项目的 `.claude/settings.json`，提交后全队共享。设置文件是严格的 JSON，不能有注释和多余逗号：

```json
{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "permissions": {
    "allow": [
      "Bash(npm run lint)",
      "Bash(npm run typecheck)",
      "Bash(npm run test *)",
      "Bash(git status)",
      "Bash(git diff *)",
      "Bash(git log *)"
    ],
    "ask": [
      "Bash(git push *)",
      "Bash(npm install *)",
      "Edit(./.github/workflows/**)"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)",
      "Edit(./package-lock.json)"
    ]
  }
}
```

## 怎么用 / 怎么改

规则格式是 `工具名` 或 `工具名(匹配内容)`。几个关键点：

| 写法 | 含义 |
| :- | :- |
| `Bash(npm run test *)` | 以 `npm run test ` 开头的命令，包括只有 `npm run test` 本身 |
| `Bash(ls *)` 与 `Bash(ls*)` | 前者要求 ls 后有空格，不匹配 `lsof`；后者会匹配 |
| `Read(./.env)` | 当前目录下的 .env 文件 |
| `Edit(/src/**)` | 相对于设置文件所属位置的路径（项目设置里是项目根目录） |
| `Edit(//tmp/a.txt)` | 双斜杠开头才是文件系统绝对路径 |
| `Read(~/.zshrc)` | 家目录下的路径 |

1. **规则顺序是 deny、ask、allow，先命中先生效。** 一条宽泛的 deny 会挡住更窄的 allow，allow 无法给 deny 开例外。
2. **通配符放在子命令之后。** `Bash(git log *)` 只放行 git log，而 `Bash(git *)` 会放行所有 git 子命令，包括 push。末尾的 `:*` 写法与空格加星号等价，但只在结尾有效。
3. **路径规则只认 Edit 和 Read。** 给 `Write`、`NotebookEdit`、`Glob` 写路径规则不会生效。要限制写入，用 `Edit(...)`；限制读取与搜索，用 `Read(...)`。
4. **一条 Read 拒绝规则也会挡住在同一路径上用 Edit 或 Write 创建文件。**
5. **复合命令逐个子命令判断。** `Bash(git status *)` 不会因为 `git status && rm -rf x` 而放行后半段。
6. 用 `/permissions` 查看当前生效的所有规则，以及每条来自哪个文件。

个人习惯的放行规则，放到 `.claude/settings.local.json`（你的本地文件，不提交）或 `~/.claude/settings.json`（所有项目），不要塞进团队共享文件。

## 常见问题或注意事项

- **项目里的 allow 规则要等信任确认。** 提交到 `.claude/settings.json` 的 allow 规则，每位队友在首次信任该文件夹后才会生效；deny 和 ask 规则则立即生效。
- **权限规则不是沙箱。** `Read(./.env)` 能拦截内置文件工具和 `cat .env` 这类识别得出文件名的命令，但拦不住在目录上运行 `grep -r` 这样不点名文件的命令。要堵住这种缺口，需要启用沙箱。
- **写法不同就匹配不到。** `Bash(git push *)` 拦不住 `git -C . push`。deny 只能当作防护网，不要当作唯一的安全边界。
- **启动模式单独设置。** `permissions.defaultMode` 可以指定会话的起始权限模式，但 `auto` 和 `bypassPermissions` 在项目或本地设置里不生效，需要放进用户或托管设置。
- **配错了会怎样。** JSON 语法错误会让整个文件被跳过；单条规则写错只会警告并跳过。用 `/status` 看加载了哪些文件，用 `claude doctor` 看被拒绝的条目。