【Agent工坊】Claude Code Hooks实战:3个钩子让你的AI编程助手安全10倍
你用Claude Code写了1000行代码,但其中37行存在安全漏洞——因为没有人审查AI的每一次工具调用。Hooks系统就是你的自动化代码审查官。
为什么你需要Hooks
Claude Code是目前最强的AI编程助手之一,但它有一个致命弱点:它不会自我审查。
当你让Claude Code"帮我修复这个SQL注入漏洞",它会直接修改代码。但你怎么知道它不会在修复的过程中引入新的安全问题?怎么确保它不会误删关键的业务逻辑?
答案藏在Claude Code的 Hooks系统 里。
Hooks是一种事件驱动的拦截机制,允许你在AI Agent执行工具调用的前后插入自定义逻辑。用人话翻译:你可以在Claude Code每次动手之前和之后,自动运行你自己的检查脚本。
这是AI创业者必须掌握的能力——因为当你的Agent在凌晨3点自动修改生产代码时,你需要一个不睡觉的安全网。
Hooks机制速览
Claude Code支持 9种Hook事件,今天聚焦最核心的3种:
| Hook类型 | 触发时机 | 典型用途 |
|---|---|---|
PreToolUse | 工具调用之前 | 拦截危险命令、注入安全规则 |
PostToolUse | 工具调用之后 | 自动格式化、记录日志、二次校验 |
Stop | Agent决定停止时 | 最终完整性检查、生成审计报告 |
配置文件路径:~/.claude/settings.json 或项目级 .claude/settings.local.json
实战一:PreToolUse — 拦截危险命令
AI Agent最让人担心的是什么?它可能会执行 rm -rf /、DROP TABLE 或 git push --force。PreToolUse Hook就是你的最后一道防线。
场景:你想让Claude Code自由编辑代码,但禁止它操作git、修改数据库、或执行任何涉及 rm 的命令。
在 .claude/settings.local.json 中添加:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 ~/.claude/hooks/bash-guard.py"
}
]
}
]
}
}
配套Python脚本 ~/.claude/hooks/bash-guard.py:
#!/usr/bin/env python3
"""PreToolUse hook: 拦截Bash危险命令"""
import json, sys, os
# Claude Code 通过 stdin 传入工具调用信息
tool_input = json.loads(sys.stdin.read())
command = tool_input.get("command", "")
tool_name = tool_input.get("tool_name", "")
# 黑名单规则
DANGEROUS_PATTERNS = [
"rm -rf", "rm -r", "git push --force",
"DROP TABLE", "DROP DATABASE",
"chmod 777", "> /dev/sda",
"format C:", "dd if=",
":(){ :|:& };:", # fork bomb
]
for pattern in DANGEROUS_PATTERNS:
if pattern.lower() in command.lower():
# 返回 blocked 决策 + 解释
result = {
"decision": "block",
"reason": f"⚠️ 检测到危险命令模式: '{pattern}'。"
f"如有必要,请手动确认后执行。"
}
print(json.dumps(result))
sys.exit(0)
# 放行
print(json.dumps({"decision": "allow"}))
效果:当Claude Code想执行 git push --force origin main 时,Hook会直接拦截并返回原因,Agent会收到反馈并调整策略。
实战二:PostToolUse — 自动格式化 + 日志审计
PreToolUse负责"拦截",PostToolUse负责"善后"。AI改完代码后,你需要确保格式规范、没有引入明显bug。
场景:每次Claude Code修改Python文件后,自动运行 black 格式化,再运行 ruff check 静态分析,最后记录所有变更到审计日志。
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "python3 ~/.claude/hooks/post-edit.py"
}
]
}
]
}
}
~/.claude/hooks/post-edit.py:
#!/usr/bin/env python3
"""PostToolUse hook: 格式化 + 静态检查 + 审计日志"""
import json, sys, subprocess, time
from pathlib import Path
tool_input = json.loads(sys.stdin.read())
file_path = tool_input.get("file_path", "")
if not file_path or not file_path.endswith(".py"):
print(json.dumps({"decision": "allow"}))
sys.exit(0)
results = []
# 1. 自动格式化
if Path(file_path).exists():
r = subprocess.run(
["black", "--quiet", file_path],
capture_output=True, text=True, timeout=15
)
results.append(f"Black: {'OK' if r.returncode==0 else r.stderr[:100]}")
# 2. 静态分析
r = subprocess.run(
["ruff", "check", "--select=E,F", file_path],
capture_output=True, text=True, timeout=15
)
if r.returncode != 0:
results.append(f"⚠️ Ruff: {r.stdout.strip()[:200]}")
else:
results.append("Ruff: PASS")
# 3. 审计日志
audit_log = Path.home() / ".claude" / "audit.log"
with open(audit_log, "a") as f:
f.write(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] "
f"Edited: {file_path} | {results}\n")
print(json.dumps({
"decision": "allow",
"systemMessage": f"🔧 Post-edit 检查完成: {'; '.join(results)}"
}))
效果:每次AI编辑代码,你的项目自动保持PEP 8格式,任何语法错误会被立即捕获,所有操作留有审计轨迹——就像给AI配了一个不睡觉的代码审查官。
实战三:Stop Hook — 最终完整性检查
Stop Hook是Agent决定"我完成了"时触发的最后一道关卡。此时你可以运行测试套件、检查TODO残留、验证关键文件未丢失。
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "python3 ~/.claude/hooks/stop-check.py"
}
]
}
]
}
}
~/.claude/hooks/stop-check.py:
#!/usr/bin/env python3
"""Stop hook: 最终完整性检查"""
import json, sys, subprocess
from pathlib import Path
issues = []
# 检查1: 是否有未提交的TODO
for py_file in Path(".").rglob("*.py"):
content = py_file.read_text()
if "TODO" in content or "FIXME" in content:
lines = [f" L{idx+1}: {line.strip()}" for idx, line
in enumerate(content.split("\n"))
if "TODO" in line or "FIXME" in line]
issues.append(f"📝 {py_file} 残留TODO/FIXME:\n" + "\n".join(lines[:3]))
# 检查2: 测试是否通过
r = subprocess.run(
["pytest", "-x", "--tb=short"],
capture_output=True, text=True, timeout=60
)
if r.returncode != 0:
issues.append(f"❌ 测试失败:\n{r.stdout[-500:]}")
# 检查3: Git状态
r = subprocess.run(
["git", "status", "--porcelain"],
capture_output=True, text=True
)
changed = len(r.stdout.strip().split("\n")) if r.stdout.strip() else 0
if changed > 0:
issues.append(f"📂 未提交变更: {changed}个文件")
if issues:
msg = "## ⚠️ Stop Hook 发现问题 ##\n" + "\n\n".join(issues)
print(json.dumps({"decision": "block", "reason": msg}))
else:
print(json.dumps({"decision": "allow",
"systemMessage": "✅ 全部检查通过,可以放心提交!"}))
效果:Agent告诉你"任务完成"时,Stop Hook会跑一遍完整检查——残留TODO?测试挂了?有文件没提交?任何一项不合格,Agent就不能"下班"。
组合使用:三合一安全网
把三个Hook串联起来,你得到了一条完整的安全流水线:
用户请求 "修复登录页的SQL注入"
│
▼
PreToolUse Hook ── 拦截 rm -rf / DROP TABLE 等危险操作
│
▼
Claude Code 执行文件编辑
│
▼
PostToolUse Hook ── Black格式化 → Ruff检查 → 审计日志
│
▼
Agent 认为完成
│
▼
Stop Hook ── TODO残留检查 → pytest → git status
│
▼
✅ 全部通过 → 任务完成
❌ 发现问题 → Agent继续修复
避坑指南
经过实测,这里有几个新手最容易踩的坑:
坑1:Hook返回格式错误导致静默失败。 Hook的stdout必须是合法的JSON,且包含 decision 字段("allow"或"block")。如果JSON格式错误,Claude Code会静默跳过Hook——你的安全检查就白写了。
坑2:Python环境问题。 Hook脚本里的 python3 需要指向你安装了依赖(black、ruff、pytest)的Python环境。建议在脚本开头显式指定完整路径或使用虚拟环境。
坑3:超时设置。 Hook执行时间过长会被Claude Code强制终止。PostToolUse 建议10秒内完成,Stop 可以放宽到60秒。在脚本中尽量避免网络请求。
坑4:Hook路径必须用绝对路径。 ~/.claude/hooks/xxx.py 中的 ~ 不会被自动展开,必须写成 /home/username/.claude/hooks/xxx.py 或用 $HOME。
坑5:循环陷阱。 如果你的PostToolUse Hook自己也调用了文件编辑工具,可能会触发新的PostToolUse,形成无限循环。解决方案:在Hook中检查环境变量 SKIP_HOOKS=1 来跳过递归。
# 在Hook脚本开头添加递归保护
import os
if os.environ.get("SKIP_HOOKS") == "1":
print(json.dumps({"decision": "allow"}))
sys.exit(0)
总结
这三组Hook加起来不到150行代码,但它们在每次AI Agent操作时都会自动运行审查——这就是自动化质量控制的核心。
对于AI创业者来说,Hooks系统有三层价值:
- 安全兜底:AI可能出错,但你的Hook不会漏检
- 规范统一:无论谁用Claude Code,产出的代码都经过同一套检查
- 可审计:每条变更都有日志,出问题能追溯
行动建议:现在就打开你的
.claude/settings.local.json,先把PreToolUse的rm -rf拦截加上。这是10行配置就能避免的灾难。
#AI创业 #Agent工坊 #ClaudeCode #Hooks #一人公司
本文由AI辅助创作,经人工审核编辑发布