那個 hook 從未抵達模型
8 月 31 日,Token Security 在 The Hacker News 上逐一介紹了 Anthropic 為本機 Claude Code 工作階段新推出的 Compliance API 端點。它們是實實在在的進步,而那篇文章對於「記錄在哪裡停下」講得很精確:在工具真正執行前就發動的 hook、躺在磁碟上的檔案、在工作階段之外啟動的行程,以及一切由 Bedrock 或 Vertex 提供服務的東西。上週那隻 npm 蠕蟲,一輩子都活在那道縫隙裡。Bromure Agentic Coding 把探針接在 VM 與外界之間的那條線上,所以記錄下來的是實際發生了什麼,而不是有人對模型說了什麼。
一份記錄只能裝下有人對模型說過的話。一場代理工作階段裡有趣的那一半,正是 沒有人說出口的那一半。
今年大半時間裡,一位想知道 Claude Code 在某台筆電上到底做了什麼的工程主管, 只有兩個選擇:讀開發者的終端回捲紀錄,或是買一套第三方包裝工具。Anthropic 的 合規端點涵蓋 claude.ai 和 Claude Desktop,對 Claude Code 幾乎沒有著墨。
這在 8 月 11 日改變了。Anthropic 為本機工作階段推出了三個 Compliance API 端點:
一個列出工作階段,一個取得某個工作階段的中繼資料,第三個則從
GET /v1/compliance/apps/sessions/local/{session_id}/messages 拉出完整記錄。
8 月 31 日,Token Security 在 The Hacker News 發表了
一篇說明它們能給你什麼的導覽。
那篇文章由 Token Security 贊助,但它依然是我讀過談「一份日誌的視界在哪裡」 最細心的說明之一。
三種區塊,以及它們涵蓋了什麼
記錄以三種區塊的形式送達。用文章自己的話說:「凡是傳達給模型的內容,都會被
記錄成三種區塊型別:text、tool_use 與 tool_result。三者合起來涵蓋了使用者
提示、bash 指令、讀取與寫入,甚至包括 MCP 指令。」
這已經很多了:模型選過的每一條 shell 指令、它讀過的每一個檔案、它呼叫過的每一個 MCP 工具。如果你的問題是「這個代理在星期二決定做了什麼」,記錄能回答你,而且 是從 Anthropic 的伺服器回答,而不是從你正在調查的那台機器上的某個檔案。
真正在做事的是第一個子句:凡是傳達給模型的內容。
視界
有四樣東西落在那道邊界之外,文章把四樣都點名了。
Hook。 「Hook 是最清楚的例子:它們在本機執行,位於模型做出決定與工具真正 執行之間,而且可以阻止某個工具執行或某個提示被送出。」 Hook 是開發者磁碟上的 一段程式碼,在模型選定某個動作到該動作真正發生的縫隙裡發動。模型從來沒聽說過 它,所以記錄也從來不會載著它。
磁碟。 「兩者都看不到磁碟上放著什麼:設定檔、已安裝的 skill 與外掛及其 .md 檔(除非它們在某次工作階段中被用到),或是在工作階段之外啟動的行程。」 一個你裝好卻從沒喚起過的 skill 不會留下任何痕跡,代理一小時前啟動又忘掉的 背景行程也一樣。
其他模型供應商。 「如果你在非 Anthropic 的模型上執行 Claude Code,你完全 拿不到 Compliance API 的涵蓋範圍,因為它只記錄與 Anthropic 模型的互動。跑在 Bedrock、Foundry 或 Google Cloud 上的工作階段不會被涵蓋。」 涵蓋範圍止於 Anthropic 的模型,於是 Codex、Gemini CLI、Grok,以及你的團隊下一季會裝的任何 東西,都被留在外面。
權限決定。 開發者對一條危險指令的核准,或是讓整段提示直接通關的繞行旗標, 都落進 OpenTelemetry,而不是合規記錄。
這一切都是正確的行為。一份錨定在模型上的日誌,記下的就是模型看見的東西。給你 下一次安全審查的問題是:你最需要看見的那些東西,落在這條線的模型那一側嗎?
上週那隻蠕蟲就住在那道縫隙裡
四天前,一個每週下載量超過 15 萬次的 npm 程式碼產生器,開始夾帶憑證竊取程式。 我們 在星期六寫過那條發布鏈: 一位陌生人在某個 pull request 上留言,專案自己的發布工作流讀了那則留言、卻沒有 確認是誰寫的,於是十個帶簽章的版本就送出去了。
把那份惡意程式再讀一次,另一隻手上拿著合規記錄。追一下每個步驟會出現在哪裡。
代理執行 npm install。那是一個 tool_use 區塊,記錄裡有。之後的一切都是
子行程。
第一波根本沒有宣告任何安裝腳本。它的酬載藏在 binding.gyp 裡,也就是
node-gyp 會讀的建置描述檔,位於 node-gyp 會拿 Python 去求值的 conditions
欄位中。模型從來沒有為它輪到過一次,也沒有任何工具呼叫點到它的名字:一段建置
檔案裡的 Python 運算式,以那條被記錄成再平常不過的指令的孫行程身分執行。
跟著來的掃描,在家目錄底下走過超過 150 個 glob 樣式,尋找 SSH 金鑰、.env
檔案、雲端憑證和登錄檔權杖。這一切,沒有人問過模型半句。
然後是常駐。在每一個能碰到的儲存庫裡,蠕蟲寫下一個 .claude/settings.json,
裡面帶著一個 SessionStart hook,每當有開發者在 Claude Code 裡打開這個專案,
就執行 setup.mjs。一個 hook、放在磁碟上、在任何人徵詢模型之前發動:四個盲點
裡的兩個,擠在同一個檔案裡。挑中那個位置的攻擊者,幾乎肯定從沒讀過任何廠商的
合規更新日誌。
整段敵意窗口持續了三小時十一分鐘,其中沒有任何一部分抵達過模型。
Bromure 把探針放在哪裡
Bromure Agentic Coding 讓每個工作區以自己的虛擬機器跑在 Apple Silicon 上,離開 那台 VM 的一切都會經過一個 proxy 和一個虛擬交換器,而它們住在 Hypervisor 的 Mac 那一側。交換器把 VM 的 80 埠和 443 埠流量導進 proxy,不需要設定任何環境 變數,客體也沒有任何東西可以關掉它。因此這份記錄不依賴代理願意配合,也不依賴 代理知不知道 proxy 在那裡。
在 Activity only 這個追蹤層級以上,每一個請求都會得到一筆中繼資料紀錄:
時間戳記、主機、埠、方法、路徑、狀態碼、延遲、在任何憑證替換之前量到的請求
位元組數、回應位元組數、proxy 在送出時替換掉了哪些憑證,以及當請求帶著一個
並非 Bromure 簽發的 bearer 權杖時的警告。不論這個請求來自代理、來自某個
postinstall 腳本、來自某個自顧自忙碌的 MCP 伺服器,或來自某人三小時前啟動
後就忘掉的行程,這筆紀錄都存在。
Security Timeline 視窗(Window → Security Timeline…)就在旁邊,回答的是另一
個問題。Trace Inspector 告訴你代理送出了什麼;時間軸告訴你 Bromure 的引擎決定了
什麼。一張依時間排列的表格,最新的在最上面,以顏色區分:綠色代表放行,紅色
代表阻擋,藍色代表資訊——憑證代管、防火牆、供應鏈、護欄、提示注入、憑證使用、
上游 TLS。它在記憶體裡保留 5,000 列。持久的副本則是
~/Library/Application Support/BromureAC/traces/ 底下加密過的工作階段追蹤檔,
以與你工作區機密相同的鑰匙圈金鑰用 AES-GCM 封起來。
同一次安裝,在 Bromure 工作區裡讀起來是這樣。
那些列裡沒有一列需要酬載曾經經過模型,也沒有一列能被 VM 內部的程式碼改寫, 因為寫下它們的引擎跑在 Mac 上,而客體沒有任何通往它們的路徑。
不論你跑的是哪個代理,都是同樣那些列
一個團隊統一用 Claude Code。接著有人為了某個專案帶進 Codex,平台組為了資料 落地把一個工作區導向 Bedrock,而一位研究員開始跑 Grok。按照文章自己的說法, 這四個裡有三個在 Compliance API 中什麼都不會產生。
一個讀取 TLS 伺服器名稱的 proxy,用同一種方式看待這四個,而同樣的
llm.request、tool.use、command.run、file.read 與 file.write 事件會從
每一個裡面出來——是從流量中抽出來的,而不是某家廠商賞給你的。在一台已加入
bromure.io 工作區的 Mac 上,它們會透過雙向 TLS 串流到組織,一併還有每個防火牆
判定的 egress.firewall、當某個誘餌憑證流向從未為它簽發的主機時的
credential.exfiltration,以及工作區拉取的每一個套件的 supply_chain.fetch。
最後這一項,就算把所有供應鏈強制層都關掉也照樣發出,因為 Bromure 把「看」和
「擋」分開了。這道串流不帶原始提示。它回答的是代理做了什麼,而不是開發者
問了什麼。
文章收尾的那一句
全文最鋒利的一句話,把涵蓋範圍拋在了身後:「光靠活動日誌,無法告訴你一個代理 的存取是否正當。」
這對任何日誌都成立,也正因如此,Bromure 的引擎自己寫時間軸,而不是把這件事 交給另一個旁觀者。那個視窗裡每一列紅色,記下的都是一個已經發生的決定。一列 供應鏈,意味著一個代理從未收到的套件。一列防火牆,意味著一條從未打開的連線。 護欄那一列是主機以 403 拒絕的推送,而代理把它讀成一次普通的 API 失敗;憑證 代管那一列則是一個從未變成真正機密的誘餌——因為真正的那一份從來就不在 VM 裡。
策略仍然得由你來定。但你回答「這次存取正當嗎」的時機,是在設定工作區的當下, 而不是幾個星期後坐著讀那些列。
把你的探針放在哪裡
如果你在任何規模上跑編碼代理,就把 Compliance API 打開。四個星期前,這個面向 幾乎毫無可見度,而一個星期的記錄會告訴你一些關於自己團隊、你原本不知道的事。
然後問第二個問題:一台記錄看起來乾乾淨淨的機器,你要拿它怎麼辦?在 Bromure
工作區裡,答案已經在螢幕上了。要看主機就打開 Window → Trace Inspector,
要看判定就打開 Window → Security Timeline,或執行
bromure-cli trace hostnames,把那個工作區自開機以來連過的每一個網域讀一遍。
代理不必提起任何一個字,它們也在那裡。 安裝 Bromure Agentic Coding,打開時間軸,看一次安裝。