你寫了 allow 它照樣擋:Claude Code 危險 rm 安全網,從一直等你回答到兩分鐘沒人理就自己拒絕
settings.json 的 permissions.allow 放行了 Bash,PreToolUse hook 也乖乖回了 "allow",那條砍家目錄的 rm -rf 照樣被 Claude Code 攔下來問你。反過來,在 9 月 23 日以前,有一種寫法的 rm -rf 在 auto 模式底下連問都沒問,直接就跑了。
你允許的它不放,你沒允許的它放過。兩件事聽起來像同一個 bug 的兩面,其實前一件是設計,後一件才是 bug。官方在 9 月 23 日、24 日連兩天各出一版調整這張網:前一版補漏洞,後一版加倒數、擴大偵測。這篇照版本順序,把同一個動作「Claude Code 要刪一個很要命的目錄」在新舊版本各被怎麼處理,一格一格對起來。
電箱裡那顆不歸你管的開關
家裡的插座你愛怎麼接就怎麼接,延長線、轉接頭、一次插五台電器都行。總電流一超過,電箱裡的無熔絲開關照樣跳,它不看你接了什麼,也不管你覺得這樣接沒問題。
Claude Code 對刪除指令也裝了一顆。官方文件叫它 critical path 保護:rm 或 rmdir 的目標只要落在 critical path,permissions.allow 規則放行沒用,hook 回 "allow" 也沒用,連那些會跳過其他提示的模式都一樣。文件原文用的詞是 circuit breaker,說它防的是模型出錯。deny 規則倒是照常有效,命中就直接擋掉。
哪些算 critical path,基礎清單是這幾類:檔案系統根目錄、根目錄底下的頂層目錄(/usr、/etc、/data 這種)、你的家目錄、Windows 的磁碟機根目錄與頂層目錄(C:\、C:\Windows)、目前工作目錄與它的每一層上層。額外加進來的工作目錄也算,但只算刪它底下的 glob:rm -rf <dir>/* 會被抓,rm -rf <dir> 本身不算。
這顆開關最早是哪一版裝上的,我查不到。能確定的只有最近兩版怎麼改它。
9 月 23 日之前:地址封在信封裡
斷路器要跳,得先知道電流多大。刪除保護也一樣,它得先知道你要刪哪裡。
rm -rf "$(pwd)" 麻煩就麻煩在這。$(pwd) 要等指令真的執行才吐得出路徑,Claude Code 在執行前拿到的只是一個沒拆的信封,收件地址封在裡面。2.1.281 之前,這種「目標只有指令代換輸出」的遞迴刪除,在 auto 與 --dangerously-skip-permissions 模式下會不經提示直接跑。CHANGELOG 那一條開頭的動詞是 Fixed,它是被當成 bug 修掉的。
2.1.281(CHANGELOG 標 9 月 23 日)之後,這種寫法一律會問,就算你有 Bash 的 allow 規則也照問。照現行文件的描述,提示的內容滿有意思:它要 Claude 自己先把代換跑一次、把實際路徑印出來,再逐一刪那些字面路徑。信封先拆開給你看,再決定寄不寄。
真的要關掉這一條,有個環境變數 CLAUDE_CODE_DISABLE_SUBSTITUTION_RM_PROMPT=1。
會問了,然後誰來答
會問是好事。問題跟著就來了:人不在的時候誰答?
會開 auto 或 bypass 的人,很多就是要它自己跑,人根本不在電腦前。一條危險指令跳出提示,然後一直等,整個 session 就卡在那邊,等一個不會來的回答。安全機制本身變成讓工作停擺的原因。
2.1.282(CHANGELOG 標 9 月 24 日,隔一天)的做法是加倒數。CHANGELOG 只寫到「等兩分鐘,然後拒絕並附改寫提示」,下面的細節是現行文件的描述。在 auto 與 bypassPermissions 模式下,終端機的提示會出現兩分鐘倒數。倒數跑完你還沒回,Claude Code 就拒絕那條指令,同時告訴 Claude 該改用什麼做法,session 繼續往下走。提示開著的時候按任意鍵,倒數會停,提示留著等你。同一個 session 裡連續三次倒數跑完都沒人理,之後的 critical-path 刪除就不再跳提示,直接拒絕;你傳一則新訊息,計數重來。
Claude Code 根本秀不出終端機提示的場合又是另一套。-p 的非互動執行、Agent SDK session、VS Code 擴充套件的聊天面板、Desktop app,在 auto 模式下連兩分鐘都不等,當場拒絕,並要求 Claude 回報它原本想刪什麼,把刪除這一步留給人。
各模式碰到 critical-path 刪除的處置,文件給的對照是這樣:
| 權限模式 | 遇到 critical path 的 rm / rmdir |
|---|---|
default |
問你 |
acceptEdits |
問你 |
auto |
終端機裡問你,有時限;看不到終端機的地方直接拒絕 |
dontAsk |
直接拒絕 |
bypassPermissions |
終端機裡問你,有時限 |
有一處兩份來源對不太起來,得老實講。官方文件寫的是 auto 與 bypassPermissions 這套處理「requires Claude Code v2.1.281 or later」;CHANGELOG 卻把「等兩分鐘、然後拒絕」那句記在 2.1.282 底下,2.1.281 只寫了指令代換那一條。我判斷不了哪邊才準,所以兩個版本號都列著。想確定拿得到倒數,升到 2.1.282 以上兩邊都滿足。
關掉倒數的開關是 CLAUDE_CODE_DISABLE_DANGEROUS_RM_TIMEOUT=1,要設在啟動 Claude Code 的那個環境裡。關掉之後,auto 模式的 critical-path 刪除改交給分類器判斷,bypass 模式的提示則沒有時間限制,一路等到有人回答。文件沒寫 2.1.281 之前這兩個模式原本怎麼處理,所以「關掉之後的樣子就是舊版行為」這種話,我不敢講。
它防的像是空字串,不太像惡意
2.1.282 還做了第二件事:把偵測範圍放大。看新加的是哪幾種寫法,我的讀法是:它防的多半是變數剛好空掉、或值剛好指到你腳下的那一刻,不太像在防有人存心砍你的硬碟。
1 | # 文件列在擴充偵測裡(不在 2.1.282 新增的三種之內) |
第二種涵蓋的頂層名稱是 mnt、tmp、usr、Users 這一類。反斜線那條乍看跟 macOS、Linux 無關,理由是 Windows 上的 Git Bash 會把單一個反斜線讀成目前磁碟機的根目錄,所以文件讓這條檢查在所有平台都生效。
$DIR/* 這種,文件給的解法很熟悉:寫成 "${DIR:?}",變數一空 shell 就報錯中止,或者乾脆改成字面路徑。寫過 shell script 的人大概都用過 :?。
會卡住的是第三種。D=$(pwd); rm -rf "$D" 就算包成 "${D:?}",檢查照攔不誤。道理很直接:D 根本不是空的,它裝的正是你的工作目錄,或是 $(git rev-parse --show-toplevel) 給出來的 repo 根目錄。:? 防的是值不見了,這裡的危險是值太準。文件講得很白,這一型只剩一條路,寫字面路徑。所以碰到從 pwd 或 git rev-parse 來的變數,想靠 :? 讓檢查放行那一步可以直接跳過,別浪費一輪。
包起來也躲不掉。subshell (...)、大括號群組 { ...; }、$(...) 或反引號、process substitution <(...),不管是 (rm -rf ~) 這樣整條包住,還是 echo "$(rm -rf ~)" 這樣混在別的指令裡,文件說都抓得到。
把三個時間點並排,同一個動作的變化是這樣:
| 同一個動作 | 2.1.281 之前 | 2.1.281(9/23) | 2.1.282(9/24) |
|---|---|---|---|
auto/bypass 下 rm -rf "$(pwd)" |
不提示,直接跑 | 一律問,Bash allow 規則也照問 | 同左 |
| auto/bypass 的提示沒人回 | 文件沒寫 | 官方文件標的最低版本 | CHANGELOG 記載:等兩分鐘後拒絕,附改寫提示 |
$TMPDIR/mnt、D=$(pwd)、"\\" 三種目標 |
CHANGELOG 未列(推斷未涵蓋) | CHANGELOG 未列(推斷未涵蓋) | 納入偵測 |
第三列前兩格是我推的:CHANGELOG 在 2.1.282 用的字是「also flag」,讀起來是這一版才多抓這三種,但舊版到底抓不抓,沒有哪份文件直接寫。
逾時那個開關,我會留著不動
我的立場是兩個環境變數都維持預設,不去關。
留著的代價,是一條刪除沒做成、Claude 拿著改寫提示去換做法。關掉的話,兩個開關各有各的後果:關掉代換那條,rm -rf "$(pwd)" 這型又回到不問;關掉逾時,auto 模式改交分類器判斷,bypass 模式則回頭卡著等人。前者的下行是路徑剛好指到你腳下的那一次,後者在 bypass 下是一個卡著等人的 session。這兩種都比一條沒做成的刪除重,我寧可多被擋幾次。
還有一件事值得分清楚方向。3 月那篇《Claude Code Bypass 模式安全防護》寫的是自己在 settings 加 deny、自己寫 PreToolUse hook 去攔危險指令。那套現在照樣有用,deny 命中還是直接擋。可是在 critical path 這件事上,自己寫的那一層只能讓規則更嚴,它回 "allow" 打不開這道門。你那層寫得再鬆,官方這層不會跟著鬆。
什麼情況我會改口?如果你的 session 一定有人盯著螢幕,而且你寧可它卡著等你,也不要它被拒絕後自己換個寫法繼續做,那在 bypass 模式下關掉逾時、讓提示無限等,比較符合你要的東西。另一個條件是看到拒絕後的改寫提示真的把 Claude 帶去更糟的做法。這個我沒有案例可以講:我只讀了文件,沒有實際跑過文中任何一條 rm,被拒絕之後 Claude 實際會怎麼改寫,我沒親眼看過。
跳電之後,其他燈要不要亮
先把還沒解的講完。這顆斷路器是從哪一版開始有的、2.1.281 之前 auto 與 bypass 碰到 critical path 究竟怎麼處理,文件都沒寫,我也還不知道。
回到電箱。老式的跳電是整間屋子一起黑掉,等有人回家去把開關扳回來。9 月 24 日那版改的是跳了之後的事:人不在家,就只切掉出事的那一路,其他電器照常運轉,門口再留張紙條寫「這一路我先關了,原因是這個」。
我的讀法是,無人值守在這裡被當成一種正式的使用情境在設計。倒數、三次上限、看不到終端機就當場拒絕,三樣東西回答的都是同一個問題:攔下來之後,沒人在場怎麼辦。
原文來源:Permission modes:Critical paths、Claude Code CHANGELOG(兩份皆為 9 月 29 日讀取的版本)















































































































































































































