一个 Agent 能读什么文件、能执行什么命令、能调用什么工具——这些不是理所当然的。权限系统和 Hooks 共同构成了 Claude Code 的安全边界与自动化引擎。
当你第一次运行 claude 时,它默认就能读取文件、执行 Bash 命令。但如果你把 Claude Code 交给团队使用,或者在 CI 环境中跑自动化脚本,这个"默认能做什么"就成了核心问题。
Claude Code 的答案是:分层控制。权限规则(Permission Rules)负责"谁能做什么",Hooks 负责"什么时候做什么"。两层机制互为补充,共同支撑起整个安全与自动化体系。
理解这两个子系统,你才能真正掌控 Claude Code------而不是被它掌控。
先说权限系统,因为它更简单。Claude Code 的权限规则写在 settings.json 里:
{
"permissions": {
"allowed": [ "Read(*.md)", "Bash(git *)", "Bash(npm run *)" ], "denied": [ "Read(.env*)", "Bash(sudo *)", "Bash(rm -rf /)" ]
} }
规则匹配有完整的语法,核心是 工具(匹配模式) 的结构。Bash 工具还支持命令子串匹配------Bash(git *) 匹配所有以 git 开头的命令,Bash(git push) 则精确匹配 git push。
alwaysAllowRules、alwaysDenyRules、alwaysAskRules
这三个规则集合是权限系统的核心决策单元。它们的优先级是:alwaysDenyRules > alwaysAskRules > alwaysAllowRules。deny 永远优先。
// 权限检查的核心链(伪代码)
function resolvePermission(toolName, input) }
// 2. 检查 alwaysAskRules — 需要用户确认 if (matchRule(alwaysAskRules, toolName, input)) {
return { behavior: 'ask' }
}
// 3. 检查 alwaysAllowRules — 直接放行 if (matchRule(alwaysAllowRules, toolName, input)) {
return { behavior: 'allow' }
}
// 4. 走默认逻辑:模式特定检查 + 交互式询问 return tool.checkPermissions(input) }
七种权限模式
Claude Code 不只是"允许/拒绝"二分法。它定义了七种权限模式,每种对应不同的使用场景:
default 工具特定检查,未知操作弹窗询问用户
acceptEdits 自动允许文件编辑,其他操作询问
plan 只读模式,拒绝所有写操作
dontAsk 自动拒绝所有需要询问的操作(用于后台 Agent)
bypassPermissions 完全绕过权限检查(用于自动化脚本)
auto 用 AI 分类器自动决定(实验性功能)
bubble 子 Agent 专有,将权限请求向上冒泡给父 Agent
bubble 模式 是理解子 Agent 权限行为的关键。当你在主会话中启动一个子 Agent 处理后台任务时,子 Agent 的 bubble 模式会把权限请求(如"是否允许删除这个文件?")转发给主会话,由主会话的人类用户在终端里做决定。这是 Claude Code 处理"需要权限但没有终端"这个矛盾的方式。
BashTool 的命令解析器
Bash 工具的权限匹配最复杂,因为 shell 命令的形式太多了。parseForSecurity() 充当了一个 bash AST 解析器的角色,把复合命令(cd /tmp && mkdir build && ls build)拆成单个子命令,然后对每个子命令做安全分类。
// BashTool 内部的安全分类(简化)
const BASH_READ_COMMANDS = new Set([‘ls’, ‘cat’, ‘grep’, ‘find’, ‘git’, ‘head’, ‘tail’]) const BASH_WRITE_COMMANDS = new Set([‘rm’, ‘mv’, ‘cp’, ‘mkdir’, ‘touch’, ‘chmod’]) const BASH_DANGEROUS_COMMANDS = new Set([‘sudo’, ‘dd’, ‘mkfs’])
function classifyCommand(cmd: string): ‘read’ | ‘write’ | ‘dangerous’
复合命令只有在所有非中性部分都是只读时才被判定为只读。这防止了 rm -rf . && git status 这样的命令被误判为安全。
一个小细节:当 bash AST 解析失败时(比如命令里有 heredoc 或嵌套 subshell),匹配器返回
() => true------意味着总是触发 hook 运行。这是一种"宁可错杀,不可漏过"的安全哲学:太复杂的命令无法静态分析,就交给运行时钩子处理。
权限规则是静态的------写死在配置里。Hooks 则是动态的------在 Claude Code 的生命周期事件里插入自定义逻辑。
Hooks 写在 settings.json 里:
{
"hooks": {
"PreToolUse": [ { "matcher": "Bash.*", "hooks": [ { "name": "validate-bash", "type": "command", "command": "/path/to/validate-bash.sh" } ] } ], "PostToolUse": [ " } ] } ]
} }
matcher 使用 glob 模式匹配工具名称,type 决定处理器的执行方式。
四种 Handler 类型
Claude Code 的 Hooks 支持四种处理器,每种都有其适用场景:
1. Command(命令处理器)
最常用。执行一个 shell 脚本或命令,通过退出码决定行为:
#!/bin/bash
返回 0 → 允许操作继续
返回 1 → 拒绝操作
返回 2 → 停止整个 Agent 循环
打印 JSON {"updated_input": {…}} → 修改工具输入后继续
2. Prompt(LLM 处理器)
不执行命令,而是把问题抛给另一个 LLM 实例做判断。适合需要语义理解的决策:
{
"type": "prompt", "prompt": "Is this bash command safe to run in a production environment? Command: ${tool_input.command}" }
3. HTTP(远程处理器)
把事件 POST 到外部服务,适合接入现有的安全扫描管道:
{
"type": "http", "url": "https://security.internal/validate", "headers": { "Authorization": "Bearer ${SECRET}" } }
4. Agent(子 Agent 处理器)
用子 Agent 处理复杂决策。例如在安全审计 hook 里启动一个专门的分析 Agent:
{
"type": "agent", "agent": "security-auditor", "prompt": "Analyze this code change for security implications…" }
Hook 生命周期完整图谱
Claude Code 目前支持 12 种 Hook 事件,按触发时机分:
会话生命周期:
SessionStart--- 会话启动时,适合加载额外上下文Stop--- 会话结束时,适合做清理或备份SubagentStart/SubagentStop--- 子 Agent 启停
工具生命周期:
PreToolUse--- 工具执行前,权限检查的最后关口PostToolUse--- 工具执行后,适合做格式化和验证
任务生命周期:
TaskCreated--- 任务被创建时TaskCompleted--- 任务完成时
环境感知:
CwdChanged--- 工作目录变化时FileChanged--- 文件被修改时Notification--- 发送通知前
上下文管理:
PreCompact--- 上下文压缩前,适合做快照PostCompact--- 上下文压缩后
用户交互:
UserPromptSubmit--- 用户提交提示词后,适合做输入验证
PreToolUse 的.updated_input 机制
最强大的 Hook 能力之一:PreToolUse 处理器可以通过返回 updated_input 来修改工具的输入参数:
#!/bin/bash
validate-and-sandbox.sh
检查 Bash 命令,如果是 sed,自动添加 -i 的安全参数
command=\((echo "\)input" | jq -r ‘.command’)
if [[ "\(command" =~ ^sed ]]; then # 限制 sed 只能操作特定目录 new_command=\)(echo "\(command" | sed 's|/home/sangchg/project|/home/sangchg/project|g') echo '{"updated_input": {"command": "'"\)new_command"‘"}}’ exit 0 fi
exit 0 # 放行
这个能力让 Hooks 超越了纯粹的门卫角色------它们可以主动改造行为,而不只是放行或拒绝。
14 步执行管道里的 Hook 位置
回顾第五章的 Agent 循环,工具执行的 14 步管道中,Hook 出现在第 7 步(PreToolUse)和第 12 步(PostToolUse):
1. 工具查找
- 中止检查
- Zod 验证
- 语义验证
- 启动 Speculative 分类器(并行)
- Input Backfill(填充派生字段)
- ← PreToolUse Hooks(可修改输入、可拒绝、可停止循环)
- 权限解析(hook 决策 > 规则匹配 > 工具检查 > 模式默认 > 交互询问)
- 权限拒绝处理
- 工具执行
- 结果持久化(过大结果存磁盘)
- ← PostToolUse Hooks(可修改结果、可阻止继续)
- 新消息追加
- 错误分类与遥测
Hook 出现在权限解析之前,这意味着它们拥有"比规则更早介入"的能力。一个 PreToolUse hook 可以比 alwaysDenyRules 更早拒绝操作,或者在规则之外做语义级别的判断(比如"这个 git push 是否force了?")。
真正强大的用法是把权限规则和 Hooks 组合起来。规则负责粗粒度的黑白名单,Hooks 负责细粒度的语义判断:
{
"permissions": {
"allowed": ["Bash(git *)"], "denied": ["Bash(sudo *)", "Bash(rm -rf /)"]
}, "hooks": {
"PreToolUse": [ { "matcher": "Bash(git *)", "hooks": [ { "type": "command", "command": "/path/to/hook-scripts/validate-git-force.sh" } ] } ]
} }
alwaysDenyRules 挡住 sudo 和 rm -rf /,Hook 则负责在允许的 git 命令里进一步审查是否包含 --force 等危险选项。
很多用户把 Claude Code 的可扩展性归功于 MCP——连接外部服务器的能力确实很酷。但 MCP 解决的是"工具种类"的问题(如何引入新工具),而 Hooks 解决的是"行为控制"的问题(现有工具怎么被使用)。
理解这一点很重要:MCP 让你告诉 Claude"可以做什么",Hooks 让你控制"怎么做"和"什么时候做"。两者结合,才构成完整的扩展体系。
Hooks 的另一个被低估的价值是上下文节约。以前你要在 CLAUDE.md 里写"每次编辑后运行 prettier",现在一个 PostToolUse Hook 自动完成这件事,不需要在每次对话里重复提醒模型。
下一章,我们来看看 Claude Code 的另一套扩展体系——插件、Skills 和 MCP——以及它们之间的关系。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/259274.html