AIDive

我打造了未公開的Claude Code Mod,防護卻失守

AIDive · 發布

編碼 agentAI 資安

從『如果你想要』到 Tetris 的十二天

Claude Code Mods 是在 Claude Code 引擎內執行的 TypeScript 模組,以函式的形式掛鉤(hook)引擎的事件。帶領 Anthropic Claude Code 團隊的 Boris Cherny 用五個字宣布了這項功能:「Claude mods are landing now.」這則貼文收穫了 2,400 個讚,而當時已經有人在終端機裡做出了 Tetris。Tetris、Doom,還有一隻會在 Claude 執行你的測試時長大的寵物,全都畫在提示列上方,而且不消耗任何 token。

這則宣布貼文並未附上任何文件頁,而是連到一個由 Anthropic 工程師在 12 天前開的 GitHub issue,裡面寫著一個條件。Alice Poteat 寫道,社群的反應很可能會決定這項功能最終是否上線。177 則留言之後,人們已經把這個掛著旗標的執行檔操到極限,測量了它的逾時行為,甚至在上面做出了遊戲。

指標 數值
宣布貼文的讚數 2,400
GitHub issue 的留言數 177
本週版本的型別介面規模 10,700 lines
事件數(issue 討論串版本,依名詞分類) 84 on 19
48 小時內出現在 GitHub 上的 Mod 數 31

截至本文寫成時,文件頁仍回傳 404。這篇文章會介紹擴充 Claude Code 的四種方式、一個刻意做出來又刻意弄壞的實用 Mod,以及在你把 Mod 上線之前,還有哪些地方仍不穩定。

Mod 是中間的一個函式

原始碼樹用一句話定義了它:Mod 是一個 Claude Code plugin,其行為寫在一個 hooks 模組裡。一筆註冊項目就能以函式的形式掛鉤引擎的事件。落到磁碟上,那就是一個 plugin 資料夾、一份剛好指定一個模組的 hooks manifest,以及該模組本身。

每個 hook 都是三個東西的函式:$,也就是所有副作用會經過的那扇門;事件本身;以及延續(continuation),也就是你下面剩下的鏈結。Hook 會像中介軟體(middleware)一樣層層巢狀。最先註冊的那個擁有這個事件,鏈結中更下面的任何 hook 都無法抑制它。這個順序是由設定決定的,而不是由安裝順序決定。用 Alice Poteat 的話來說,順序是設定出來的,不是照安裝的先後順序。

沒有任何環境級(ambient)存取權限。Mod 所做的一切都要經過 $,因此管理員可以稽核、允許、拒絕或記錄任何事件。有位留言者做了個總結:一個 plugin 做了什麼,就精確地等於它發出了哪些呼叫。在 * 上掛一個 hook 就能看到每一個事件,所以一個稽核日誌只需要一個函式。Mod 也可以繪製畫面,因為介面用的是 React。它在 Bun 上以同一行程執行,第 99 百分位的延遲只有 50 微秒。只要一個環境變數就能開啟它。至於 $ 上究竟會放什麼,目前仍在和搶先體驗的合作夥伴一起設計中。

擴充 Claude Code 的四種方式,該用哪一種

shell hook 是引擎在固定時機呼叫的腳本。它從標準輸入取得 JSON,並以 exit code 回應。回應有上限:傳回的上下文最多 8,000 個字元,部分 hook 則只有 2,000 個字元。用 Pratham 的話來說,在 Windows 上它們常會以很奇怪的方式故障。

plugin 是一個盒子。一份 manifest 就能把 skill、agent、hook 與 MCP server 打包在一起。它可以從市集(marketplace)安裝,也可以直接從磁碟上的資料夾載入,而且在任何東西執行之前,都有一個指令可以驗證這個盒子。

skill 是模型在需要時才讀取的文字說明。內文只有在符合的提示出現時才會載入。它仍然是改變行為最便宜的方式。

Mod 就是同樣的盒子,只是多了一個檔案:一份指定模組的 hooks manifest,而那個模組是 TypeScript 寫的、具型別、在行程內執行,並掛鉤每一個引擎事件。差別就僅止於此。

機制 是什麼 執行方式 上限
Shell hook 在固定時機被呼叫的腳本 子行程,以 exit code 回應 傳回最多 8,000 chars back(部分 hook 為 2,000)
Plugin skill、agent、hook、MCP server 的組合包 安裝或從磁碟載入 執行前先驗證
Skill 符合提示時載入的文字說明 位於模型的上下文中 成本最低的改動方式
Mod Plugin 加上一個 TypeScript hooks 模組 行程內執行,涵蓋每個事件 具型別,搶先體驗

這些型別來自一個斜線指令(slash command),它會把 $ 提供的完整清單直接寫進你的專案裡。舊的 shell hook 是被包裝(wrapped)起來,而不是被淘汰。在一個早期版本上,Spencer Morley 就曾看過包裝器載入失敗,但宣告仍留在原處。經驗法則是:要改變 Claude 知道的東西,寫一個 skill;要在某個時機執行腳本,用 shell hook;要出貨一個組合包,用 plugin;要坐進引擎裡面,用 Mod。只要旗標是關閉的,shell hook 在任何地方都照樣能用。

Anthropic 自己的三個 Mod,原始碼解讀

有三個 Mod 內建於執行檔中,原始碼都在 GitHub 上:一個安全預設值、一個 diff 面板,以及遙測(telemetry)。

安全預設值位於最外層。在有受管理設定的機器上,或是在 Team 或 Enterprise 組織中,使用者安裝的任何東西都無法蓋過它。它掛鉤了 12 個事件,而每個 hook 都只會做三件事之一:放行使用者層級以外的呼叫、以名稱拒絕來自使用者層級的呼叫者,或是直接放行。它採用失效即關閉(fail closed)的設計,而原始碼裡的文件註解也只用兩個字說明了這件事。這正是討論串最在意的部分:管理員從 $ 上移除一項能力後,底下註冊的任何東西都無法再呼叫它。用某位留言者的話來說,這和「拜託一個 plugin 不要做某件事」在本質上完全不同。

Diff 是逐字稿旁的一個面板,逐檔顯示該工作階段中尚未提交的變更,並隨著 Claude 編輯即時更新。它在工作階段開始時就會註冊,程式碼橫跨 27 個輔助檔案,不是什麼玩具功能。

遙測會在引擎建立的過程中,為 $ 加上一個名詞。它會等待底下的結果,再把該結果連同自己一起回傳。目前只在內部版本中執行。

README 上寫著要從原始碼執行一個、從原始碼測試一個。但今天這個版本的說明清單裡只列出 validate、eval 與 details,清單上並沒有 test,儘管 test 的說明指令仍然有回應。內建的層級也會拒絕你自己的版本:如果你出貨的 plugin 用了這些名稱之一,執行檔會改載入它自己內建的那份。

四十二行:一個對模型隱藏機密的 Mod

在這個版本上,寫出型別的斜線指令會產生 11,700 行:23 個名詞、共 84 個事件。工具呼叫的 hook 只能以結果或拒絕來回應,永遠不能回傳原始文字,這部分是由核心固定死的。

這個 Mod 由三個檔案組成:plugin manifest、只有一行的 hooks manifest,以及模組本身。模組只有 42 行,它會等待底下的結果、清洗過後再交回去。四個樣式(pattern)就涵蓋了兩種供應商金鑰格式、一個 GitHub token,以及任何被指派給名為 key、secret 或 token 的變數的值。

validate 會在執行前先讀過原始碼,列出這個模組掛鉤了哪個事件,以及它在 $ 上呼叫了哪一項。唯一的警告是缺少作者資訊。在開啟旗標、從磁碟載入之後,測試檔裡放了兩把金鑰,兩把都是刻意造假的,而模型讀到的是「redacted」。用它自己的話來說,回傳的值已經被遮蔽,所以它看不到裡面實際的內容。

指標 數值
模組長度 42 lines
呼叫延遲(含 worker) 28 ms
工作階段新增的 token 數 0

引擎會把這次呼叫記錄為「由 hooks 模組解析完成」。清單裡沒有任何 skill、沒有任何 agent,也沒有任何常駐項目。仍有一個限制存在:對模型隱藏,不等於對畫面隱藏。逐字稿仍然會顯示該工具實際印出的內容。Ship Notes 的 Max 用一句話指出,那得靠另一個 Mod 才能解決。

打破它:太慢的防護就是被繞過的防護

同一個 Mod,只加了一行:在呼叫底下的內容之前先睡眠(sleep)15 秒。10 秒之後,引擎就放棄等待,回報這個 hook 已超出預算並被標記為 skipped,底下的內容則取而代之執行。指令照樣執行,並印出「hi」。

把 sleep 換成 throw:574 毫秒後得到同樣的結果——skipped,指令照樣執行。

情境 時間 結果
正常的 hook 28 ms Resolved
拋出例外 574 ms Skipped,指令仍執行
卡死 over 10 s Skipped,指令仍執行

兩種失敗最後都落在同一個詞上:skipped。討論串一週前就已經測量出這種不對稱性。載入時缺失的能力會失效即關閉(fail closed);而超出預算的 hook 則會失效即開放(fail open)——用 Spencer Morley 的話來說,就是「吵歸吵,但照樣被繞過」。一個沒有理由的攔阻,只會讓模型改去用另一個工具。Pratham 就親眼看過模型改選了另一個工具,照樣把檔案寫了下去。

目前檯面上的解法是加一個 catch。Alice Poteat 提議在 hook 的回傳上加一個 catch,只要你拖太久或拋出例外,它就會被執行。一次被改寫過的寫入動作,仍然需要留一則給模型的說明:出於快取的原因,模型看到的還是它原本要求寫入的內容,所以你得附加一行上下文說明。這個時間預算同時也是隔離機制:hooks worker 是獨立執行的,一旦它當機,引擎會重新啟動它,並在這個工作階段中把 hook 全部關閉。真正的修法不是拉長預算,而是宣告一個 catch。

Mods 上線四十八小時,一句話能換到什麼

上線兩天後:提示列上方出現了 Tetris,還有另外七款遊戲,可以在 Claude 工作時遊玩,而且完全不消耗 token。1993 年的原版 Doom 在自己的行程中執行,Mod 透過本機 HTTP 連上它,並以每秒 10 次的頻率重繪畫面。轉圈圖示裡還藏了一個呼吸節奏器,以及一個在 hooks worker 內執行、擁有 26 萬個參數的故事模型,同樣完全不耗用 API token。

登錄庫用 validate 掃描了 31 個 Mod。其中 14 個能執行主機行程,13 個能看到每一次工具呼叫。

社群討論串裡的第 8 號示範宣稱,一句話就能寫出一個能在模型讀取之前先隱藏機密的 plugin。我們也試著要求了一個。

指標 數值
花費時間 4 min
對話輪數 34
花費 $1.23
行數(含測試檔) 190,對照我們寫的 42 行
測試 4 個測試在三分之一秒內全部通過

它會依類別標示自己遮蔽了什麼,通過 validate 檢查且無警告,也不需要任何 API 金鑰或模型呼叫。有一則回覆這樣形容:允許清單(allowlist)才是產品,Tetris 只是展示品。

結論:現在就動手寫,晚點再上線

登錄庫自己的數字就是警訊。31 個 Mod 中有 14 個能執行主機行程,而目前唯一的審查方式僅是靜態足跡分析。eval 指令的說明本身也這麼寫:測試套件通過,不代表通過了安全審查。

訊號 數值
8 日的事件數 20
15 日的事件數 84
兩週內釋出的 CLI 版本數 14
Hacker News 分數 2
相容性問題的回覆數 0

如同 Marat 所指出的,那 10 秒的時間預算活在執行時期(runtime)裡,而不是寫在型別定義中。目前沒有任何 changelog 紀錄、沒有文件頁,也沒有正式的上線貼文。有競爭者宣稱 Anthropic 抄襲了這個概念:Ahmad Awais 指出 Command Code 的 mods 在先,而且是他親自寫了 Command Code 的範例 mod,這點值得列入考量。那則寫著「搶先體驗,API 可能會變動」的貼文獲得了 357 個讚。

如果你已經在寫 hooks,而且想要稽核、遮蔽機密或是一個面板,現在就可以動手做一個。如果你要出貨給團隊使用,那就等到這份契約(contract)真正寫定之後再說。也正因為這樣的開放性,那個會失效即開放的時間預算、MCP 呼叫的型別不匹配,以及包裝器失效的問題,才會在短短幾天內就被使用者找出來。

來源

常見問題

什麼是 Claude Code Mod?
Mod 是一種 Claude Code plugin,其行為寫在一個 TypeScript 的 hooks 模組裡。這個模組會在引擎的事件上註冊函式,並在 Bun 上以同一行程執行,所有的副作用都會經過 `$` 物件。
Claude Code 的 hook、plugin、skill 與 Mod 有什麼差別?
shell hook 是在固定時機被呼叫的腳本,以 exit code 回應。plugin 是 skill、agent、hook 與 MCP server 的組合包。skill 是模型在符合的提示出現時才載入的文字說明。Mod 則是多了一個檔案的 plugin:一個具型別的 TypeScript 模組,在行程內掛鉤引擎的每一個事件。
Claude Code Mod 可以對模型隱藏機密資訊嗎?
可以。一個 42 行的 Mod 會等待工具呼叫的結果,清除 key、secret、token 這類樣式,再回傳已遮蔽的文字,每次呼叫只多花 28 毫秒,也不消耗 token。但它無法把值從畫面上的逐字稿中隱藏,那需要另一個 Mod 來處理。
為什麼太慢的 Claude Code Mod 會失守(fail open)?
hooks worker 會給每個 hook 10 秒的時間預算。一旦超時或拋出例外,該 hook 就會被標記為 skipped,底下的鏈結會取而代之執行,原本要被擋下的指令因此照常執行。Anthropic 提出的修法是在 hook 的回傳上宣告一個 catch。
安裝 Claude Code Mods 安全嗎?
目前預設還不算安全。在最初 48 小時內被收錄的 31 個 Mod 中,14 個能執行主機行程,13 個能看到每一次工具呼叫,而目前唯一的審查方式只是靜態足跡分析。在受管理設定(managed settings)下,Anthropic 的安全預設值會置於使用者安裝的任何項目之上。
現在該動手寫一個 Claude Code Mod 嗎?
如果你已經在寫 hooks,而且想要稽核日誌、資訊遮蔽或 diff 面板,現在就可以動手。但在推送給團隊使用前最好再等等:這個功能還是搶先體驗,API 在一週內從 20 個事件暴增到 84 個,而且目前還沒有文件頁或 changelog 紀錄。

相關影片