凌晨兩點,手指一滑開了 bypass 模式,Claude Code 像脫韁的馬一路跑。跑得很順——直到它跑出一句 rm -rf 把半個 repo 幹掉。

不誇張,bypass 模式(bypassPermissions)跳過所有工具執行確認,效率直接拉滿,但副作用也很直接:AI 可能在你沒注意的時候送出 force push、hard reset,甚至砍掉整個 .git。這不是理論風險,是踩過坑的人才會去設防的東西。

這篇記錄一套雙層防護架構:permissions.deny 規則 + PreToolUse Hook 腳本,讓 bypass 模式既保留速度,又不會在關鍵時刻翻車。


第一層:permissions.deny — 粗粒度攔截

~/.claude/settings.json 直接用 pattern matching 把最危險的指令擋掉:

1
2
3
4
5
6
7
8
9
10
{
"permissions": {
"deny": [
"Bash(rm -rf *)",
"Bash(git push --force*)",
"Bash(git push -f *)",
"Bash(git reset --hard*)"
]
}
}

這層做的事情很單純——字串匹配,命中就拒絕。快,但不夠細。比方說 git push origin main(非 force 但推到主分支)就穿不過去,這時候需要第二層。

第二層:PreToolUse Hook — 細粒度正則攔截

Hook 的概念:在工具執行前觸發一支腳本,腳本拿到指令內容,用正則判斷要不要放行。

註冊 Hook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash ~/.claude/hooks/block-dangerous-commands.sh"
}
]
}
]
}
}

matcher: "Bash" 表示只攔截 Bash 工具的呼叫,不影響其他工具。

攔截腳本

腳本放在 ~/.claude/hooks/block-dangerous-commands.sh,透過 stdin 接收 JSON,用 jq 解析指令內容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#!/bin/bash
COMMAND=$(jq -r '.tool_input.command' 2>/dev/null)
[ -z "$COMMAND" ] && exit 0

VIOLATION=""

# 遞迴刪除
if echo "$COMMAND" | grep -qE '\brm\s+(-[a-zA-Z]*r|--recursive)'; then
VIOLATION="遞迴刪除 (rm -rf / rm -r) 被攔截"
fi

# Force push
if [ -z "$VIOLATION" ]; then
if echo "$COMMAND" | grep -qE 'git\s+push.*(\s-f\b|--force)'; then
VIOLATION="Force push 被攔截"
fi
fi

# 推送到主分支
if [ -z "$VIOLATION" ]; then
if echo "$COMMAND" | grep -qE 'git\s+push\s+\S+\s+(main|master|production)\b'; then
VIOLATION="推送到主分支被攔截"
fi
fi

# Hard reset
if [ -z "$VIOLATION" ]; then
if echo "$COMMAND" | grep -qE 'git\s+reset\s+--hard'; then
VIOLATION="git reset --hard 被攔截"
fi
fi

# git clean -f
if [ -z "$VIOLATION" ]; then
if echo "$COMMAND" | grep -qE 'git\s+clean\s+.*-f'; then
VIOLATION="git clean -f 被攔截"
fi
fi

# 倉庫刪除
if [ -z "$VIOLATION" ]; then
if echo "$COMMAND" | grep -qE '(gh\s+repo\s+delete|rm\s+.*\.git\b)'; then
VIOLATION="倉庫刪除操作被攔截"
fi
fi

# dd 指令
if [ -z "$VIOLATION" ]; then
if echo "$COMMAND" | grep -qE '\bdd\s+'; then
VIOLATION="dd 指令被攔截"
fi
fi

# 攔截 → 寫 log + deny
if [ -n "$VIOLATION" ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') BLOCKED: $VIOLATION$COMMAND" \
>> ~/.claude/blocked-commands.log
jq -n --arg reason "$VIOLATION" '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: $reason
}
}'
exit 0
fi

exit 0

腳本邏輯很直觀:拿到指令 → 跑一輪正則 → 命中就輸出 permissionDecision: "deny" 加上原因,Claude 會收到拒絕訊息然後換條路走。沒命中就 exit 0 放行。


攔截了什麼?一張表看完

攔截項目 匹配邏輯 風險等級
rm -rf / rm -r 遞迴刪除 🔴 極高
git push --force / -f 強制推送覆蓋歷史 🔴 極高
git push origin main/master 直推主分支 🟠 高
git reset --hard 硬重置丟失變更 🔴 極高
git clean -f 強制清除未追蹤檔案 🟠 高
gh repo delete / rm .git 刪除整個倉庫 🔴 極高
dd 磁碟層級寫入 🔴 極高

正常的 git push(非 force、非主分支)不會被攔截。lscatnpm install 這些日常指令更不會被碰。


Hook 的三種決策

PreToolUse Hook 輸出的 permissionDecision 有三個選項:

  • deny — 立即攔截,Claude 看到拒絕原因後會自動調整策略
  • allow — 明確放行(bypass 模式下通常不需要)
  • ask — 暫停,提示使用者手動確認

大部分場景用 deny 就夠了。如果有些指令「可能危險但不確定」,可以用 ask 讓人介入判斷。


稽核記錄

每次攔截都會寫進 ~/.claude/blocked-commands.log

1
2
2026-03-17 14:30:00 BLOCKED: Force push 被攔截 — git push --force origin main
2026-03-17 15:12:33 BLOCKED: 遞迴刪除 (rm -rf) 被攔截 — rm -rf /tmp/build

定期翻一下 log,可以看出 AI 在什麼情境下會嘗試危險操作。這些 pattern 回頭可以拿來優化 prompt 或調整 rules。


測試方式

部署完先跑兩組測試確認行為正確:

1
2
3
4
5
6
7
# 應該被攔截
echo '{"tool_input":{"command":"rm -rf /"}}' | \
bash ~/.claude/hooks/block-dangerous-commands.sh

# 應該放行
echo '{"tool_input":{"command":"ls -la"}}' | \
bash ~/.claude/hooks/block-dangerous-commands.sh

第一組會輸出 deny JSON,第二組靜默通過。


限制與注意事項

這套防護是應用層的,有幾個邊界要認知到:

  1. 間接呼叫繞得過去eval "rm -rf" 或透過 script 檔間接執行,正則抓不到。要完全防堵得搭配 OS 層級的 sandbox(Docker、cgroup)。
  2. 腳本依賴 jq — 沒裝 jq 的環境會直接放行所有指令,記得先確認。
  3. 不防 Claude Code 以外的操作 — 你自己在 terminal 打 rm -rf,Hook 管不到。
  4. 正則不是萬能的 — 指令拼接、變數展開、alias 都可能繞過。但對於 AI Agent 生成的標準格式指令,覆蓋率已經相當高。

在正式環境放著不管,等於給 AI 一把沒上鎖的刀。這套 Hook 不算完美,但至少在 AI 伸手碰危險操作的那一刻,會先被擋下來問一句「你確定?」

原文素材來源:開發知識庫 — Claude Code Bypass 模式安全防護
參考來源:Claude Code in Action - Anthropic Academy