供應鏈防護
自主編碼代理程式會不斷安裝套件——搭建一個專案就會拉進數百個相依套件,過程中沒有任何人讀過套件名稱,更別說變更紀錄了。這使得套件登錄表成為敵意程式碼進入沙盒的最寬廣單一通道:一個仿冒拼寫的名稱、一個剛被劫持的發行版本,或一段惡意的 postinstall 指令碼,都會在 npm install 完成的那一刻,以代理程式的完整權限執行。Bromure Agentic Coding 將每一次套件抓取都視為不受信任的輸入,並在主機端對其套用各機器專屬的政策,讓任何一個位元組都無法進入 VM 之前先攔下。
本章說明哪些內容會被攔截、管線中的每一項檢查、檢查觸發時會發生什麼事,以及如何在安全性時間軸中解讀結果。供應鏈政策是以機器為單位設定的(在設定編輯器中即一個工作區)。此窗格逐欄位的設定參考位於供應鏈設定。
為什麼套件安裝是攻擊面
沙盒本身已經把代理程式關住了:它無法碰觸你的 Mac,而且它的憑證都是誘餌(見憑證)。沙盒本身無法做到的,是判斷代理程式拉進來的程式碼的來源出處。供應鏈攻擊正是利用這個缺口:
- 新發行版本劫持。 維護者的帳號被入侵,並發布了一個惡意版本。這類版本通常會在數小時到數天內被偵測並撤下——這正是為什麼時效閘門會拒絕比某個截止時間更年輕的版本。
- 已知有漏洞的版本。 代理程式將某個相依套件解析到一個有已發布公告的版本。OSV 檢查會攔下這些。
- 惡意程式、仿冒拼寫與流氓安裝指令碼。 一開始就被構建成惡意的套件。socket.dev 與 Depi 供應商會標記或過濾這些,而安裝指令碼剝除則直接移除了最常見的執行途徑。
強制執行的設計就是在主機端進行。代理伺服器會在代理程式看到回應之前先對其套用政策,因此在 VM 內執行的任何東西——包括一個完全被入侵的代理程式——都無法放寬規則。VM 內的 .npmrc 與 pip.conf 只能進一步限縮代理伺服器已經提供的內容,永遠無法放寬。信譽服務的 API 金鑰(socket.dev、Depi)僅保存在主機端,永遠不會匯出到 VM 中。
攔截如何運作
VM 發出的每一個網路請求都會經過主機端的 MITM 代理伺服器(見概念)。代理伺服器會辨識送往主要套件登錄表的請求,並將每一個請求分類:
- 中繼資料——一個套件的版本清單(npm packument、PyPI 的 JSON API 或
/simple/索引、Cargo 稀疏索引項目等等)。時效閘門在此運作,透過改寫清單來實現。 - 成品——某個特定版本的可下載檔案:npm 的
.tgz、Python 的 wheel 或 sdist、.crate、.gem、.nupkg,或 Go 模組的.zip。OSV 查詢、socket.dev 檢查、指令碼剝除與硬性阻擋,全都在抓取成品時發生。 - 直接放行——這些主機上的其他一切(搜尋、驗證)。原封不動。
不論政策為何,每一次套件抓取都會被分類並記錄在安全性時間軸中;檢查本身則只會針對你啟用的層級執行。VM 內部無需安裝或設定任何東西。
附註: 已存在於 npm 或 pip 本機快取中的套件從不會觸及網路,因此不會對它們做任何檢查——也不會記錄。安裝期間時間軸一片安靜,可能只是代表一切都來自快取。
判決結果:允許、改寫、阻擋、保留
供應鏈各層是個別的開關切換,每一個都有固定的動作——此窗格沒有全域的阻擋/警告/允許模式。(你可能在提示注入中認識的三向記錄/詢問/阻擋單選鈕,以及防護欄中的強制執行模式,都是各自獨立的系統。)每一次抓取最終都會落入以下四種結果之一:
| 結果 | 發生什麼事 | 可見標記 |
|---|---|---|
| 已允許 | 回應原封不動地通過。 | 安全性時間軸中綠色的已檢查 / 已下載列。 |
| 已改寫 | 代理伺服器修改了回應:從中繼資料移除過新的版本,或從 tarball 剝除安裝指令碼。套件管理員照常繼續。 | X-Bromure-Rewritten: supply-chain 回應標頭;時間軸中的藍色列。 |
| 已阻擋 | 下載以 HTTP 451(「因法律原因無法提供」)回應被拒絕。 | X-Bromure-Block: supply-chain 標頭;時間軸中的紅色列。 |
| 保留待同意 | 下載暫停,同時 Bromure 詢問你——用於鎖定檔釘選的直接放行、沒有任何啟用的信譽來源能審查的套件,以及被 OSV 或 socket.dev 標記的套件(見信任被封鎖的套件)。拒絕會將保留轉為 451 阻擋。 | 系統同意警示;該決定會被記錄。 |
451 阻擋會攜帶一個純文字內文,開頭為 Bromure Supply-Chain Security blocked this request:,後接確切原因,例如:
Bromure Supply-Chain Security blocked this request: npm package [email protected]
published 4 hours ago — policy requires 2 days minimum
npm、pip、cargo 與其他套件管理員會在其錯誤輸出中一字不差地印出該內文,因此代理程式與你都能精確看到安裝失敗的原因——而且代理程式通常可以自行繞過它(例如,釘選一個較舊的版本)。之所以刻意選用 451 狀態,是為了讓供應鏈阻擋能一眼與防護欄使用的 403 區分開來。
各生態系的涵蓋範圍
代理伺服器攔截八個套件生態系。並非每一項檢查都支援每一個生態系:
| 生態系 | 攔截的主機 | 時效閘門 | OSV | socket.dev | Depi | 指令碼剝除 |
|---|---|---|---|---|---|---|
| npm | registry.npmjs.org、*.npmjs.org | 是 | 是 | 是 | 是 | 是 |
| PyPI | pypi.org、files.pythonhosted.org | 是 | 是 | 是 | — | — |
| Cargo | crates.io、static.crates.io、index.crates.io | 是 | 是 | 否 | — | — |
| RubyGems | rubygems.org | 是 | 是 | 是 | — | — |
| Maven Central | repo1.maven.org、repo.maven.apache.org、search.maven.org | 否 | 是 | 是 | — | — |
| NuGet | api.nuget.org、*.nuget.org | 否 | 是 | 是 | — | — |
| Go modules | proxy.golang.org | 否 | 是 | 是 | — | — |
| Packagist | repo.packagist.org、packagist.org | 是 | 是 | 是 | — | — |
有兩個值得知道的缺口:
- 時效閘門——Maven、NuGet、Go。 這三個生態系在其標準中繼資料回應中不攜帶各版本的發布時間戳記,因此其中繼資料會不經過濾地通過,而成品後援也沒有資料可依據來動作。時效閘門今天實際上不會阻擋它們;npm、PyPI、Cargo、RubyGems 與 Packagist 則完全涵蓋。
- socket.dev——Cargo。 socket.dev 不支援 Cargo。啟用 socket.dev 過濾後,每一個
crates.io成品都得不到判決,因此會觸發未經驗證套件的同意提示(見離線與降級行為)。如果你的機器有大量 Rust 工作,可以選擇回應這些提示,或改用其他供應商。
時效閘門
時效閘門會拒絕比可設定天數更年輕的套件版本,其理論基礎是:一個剛發布的發行版本最有可能是剛被劫持的套件——惡意版本通常會被快速回報並撤下,而等過那段時間對一般開發而言幾乎不花成本。它是唯一預設啟用的層,最小時效為 2 天。
它透過兩個相互配合的機制運作:
- 中繼資料改寫。 代理伺服器會從登錄表的版本清單中剝除過於新鮮的版本,並將
latest與其他 dist-tag 重新指向最新的存活版本。浮動參照——pkg@latest、semver 範圍——因此永遠不會被硬性阻擋:套件管理員會靜默地解析到足夠老、足以通過的最新版本。從代理程式的角度來看,比截止時間更年輕的版本根本尚不存在。 - 抓取成品的後援。 代理伺服器在中繼資料中看到的每一個各版本發布時間都會被快取在記憶體中(最多 50,000 個項目)。如果代理程式接著直接請求一個釘選的、過於新鮮的版本,該抓取會被 451 阻擋,並說明套件的實際時效與所需的最小值。對於 pip——其預設的 PEP 503 HTML 索引不攜帶時間戳記——Bromure 會即時查詢
https://pypi.org/pypi/<pkg>/<version>/json以取得發布時間。
若要設定,請開啟機器的供應鏈窗格並使用時效閘門群組:
- 切換拒絕比截止時間更年輕的套件。
- 以步進器設定最小時效:(0–90 天)。
- 如果某個特定套件必須能立即安裝——例如,是你自己的團隊發布的——用新增項目按鈕將它加入豁免套件之下。項目使用允許清單格式:
npm:axios將豁免範圍限定於一個生態系,而裸的axios會在每個生態系中比對該套件名稱。比對不分大小寫。
提示: npm 的中繼資料請求會被靜默升級為完整的 packument,好讓閘門能看到發布時間——對於 npm 的縮寫中繼資料格式,你不需要做任何事。
OSV 漏洞檢查
OSV 檢查會在 api.osv.dev——免費的開源漏洞資料庫,它匯總了 GitHub Advisory Database、PyPI 公告、Go 的漏洞資料庫、RubySec 等——上查詢每一個下載成品的生態系、套件與版本。如果該確切版本有任何公告達到或超過你所選的嚴重度,該下載就會被 451 阻擋。它涵蓋全部八個生態系,且不需要 API 金鑰。
當公告帶有 GHSA 標籤時,嚴重度取自該標籤;否則 Bromure 會從公告的向量字串計算 CVSS v3 基本分數。
在 OSV 漏洞檢查群組中:
- 切換在 api.osv.dev 上查詢套件(免費、不需金鑰)。
- 選擇在此嚴重度阻擋:——低度以上、中度以上、高度以上,或僅限重大。
此檢查預設關閉,而嚴重度門檻預設為高度以上——正如窗格本身所指出的,一個遞移子套件中的低度嚴重性 CVE 不應中斷工作流程。查詢僅在成品下載時執行(而非中繼資料抓取),結果會在應用程式執行期間快取在記憶體中,最多同時執行 16 次查詢,而暫時性的網路錯誤最多會以退避方式重試 5 次。如果完全無法連上 OSV,該套件會被保留以待你的同意,而非被靜默允許——見離線與降級行為。
套件過濾:socket.dev 與 Depi
套件過濾群組透過一個互斥的單選鈕選擇信譽供應商:無、socket.dev 或 Depi。這兩個供應商的運作方式截然不同——socket.dev 是代理伺服器在放行抓取前先諮詢的預先查詢;Depi 則直接取代 npm 登錄表。設計上一次只能有一個作用(同時執行兩者會對每次安裝雙重過濾),而選擇無會停用兩者,同時保留任何已儲存的金鑰。無論你選擇哪個供應商,本章中的其他各層——時效閘門、OSV、指令碼剝除——仍會在其之上套用。
預設是無。在單選鈕存在之前建立、且已有 socket.dev 金鑰的工作區,會自動被推斷為 socket.dev。
socket.dev
socket.dev 是一項商業套件信譽服務。選擇 socket.dev 並輸入 API 金鑰後,代理伺服器會在提供每一個成品下載之前,先對照 socket.dev 的問題 API 進行檢查。它支援 npm、PyPI、Go、Maven、RubyGems、NuGet 與 Packagist——但不支援 Cargo(見各生態系的涵蓋範圍)。
你需自備金鑰:點擊 API 金鑰:欄位旁的取得 API 金鑰(它會開啟 socket.dev/dashboard/settings/api-tokens),建立一個權杖,然後將它貼入安全欄位。在輸入金鑰之前,兩個阻擋切換都會保持停用,而空白金鑰會使 socket.dev 完全停用,無論切換為何。金鑰儲存在主機端工作區的 profile.json 中,永遠不會進入 VM;所有查詢都源自你的 Mac。
有兩個獨立的阻擋可用:
- 阻擋已遭入侵的套件(流氓安裝指令碼、被標記為惡意程式、仿冒拼寫、可疑遙測)——針對 socket.dev 攻擊形態的供應鏈風險問題觸發。確定性的惡意程式訊號(惡意程式、已知惡意程式、GPT 偵測到的惡意程式、惡搞套件、遭入侵的 SSH 金鑰)會在任何嚴重度都阻擋。較嘈雜的攻擊形態訊號——混淆的程式碼、可疑字串、安裝指令碼、仿冒拼寫、shell 存取、異常的 HTTPS 使用——只在 socket.dev 將其評為高度或重大時才阻擋,因此帶有無害
postinstall的套件不會被誤抓。純品質與屬性訊號(新作者、環境變數讀取、網路存取、遙測)刻意永遠不阻擋。 - 阻擋帶有已知 CVE 的套件——針對 socket.dev 的漏洞分類,在達到或超過 CVE 阻擋門檻:選擇器(與 OSV 相同的四個層級;預設高度以上)時觸發。
兩種阻擋預設皆為關閉。結果會在應用程式執行期間快取,最多同時平行執行 16 個呼叫,暫時性失敗會以退避方式重試 5 次。驗證錯誤會連同 HTTP 狀態碼與回應本文預覽一併寫入應用程式日誌。
附註: 如果你同時啟用 OSV 與 socket.dev 的 CVE 阻擋,兩者都會檢查每一個成品——這是允許的(它們是不同的層),只是在 CVE 涵蓋上有所重複。許多使用者會改為將 socket.dev 的已入侵套件阻擋與免費的 OSV 檢查搭配使用。
Depi
Depi 是由 Lupin & Holmes(landh.tech)提供的一個直接替換式安全 npm 登錄表,它提供預先審查過的套件。Bromure 不是去查詢套件,而是將每一個 npm 登錄表請求——中繼資料、tar 封存檔、稽核,任何送往 registry.npmjs.org 或 *.npmjs.org 的請求——重新路由到 Depi 位於 depi-npm-proxy.landh.tech:443 的 npm 相容過濾登錄表,將你的金鑰附加為 Authorization: Bearer 標頭,並剝除客體送出的任何 Authorization 標頭。Depi 會在其 packument 中改寫 tar 封存檔的 URL 以指向自身,因此那些後續抓取也會在主機端被注入金鑰。
若要啟用,請在單選按鈕中選擇 Depi,並將你的金鑰貼到 API 金鑰: 安全欄位中(取得 API 金鑰會開啟 landh.tech)。欄位為空時,會出現一則橘色警告:請輸入 API 金鑰——沒有金鑰時 Depi 會保持關閉。 每一個被重新導向的請求都會寫入應用程式日誌:
[depi] GET registry.npmjs.org/… → https://depi-npm-proxy.landh.tech
錯誤處理是明確的,而非靜默的:
- 401(金鑰被拒絕)。 Bromure 會替換為一個 npm 會印出的清晰純文字錯誤(「Bromure: the Depi registry rejected the configured API key…」,帶有標頭
X-Bromure-Block: depi-auth),將其記錄下來,並針對每一個工作區與金鑰的組合觸發一次性的 GUI 警示:「Depi 拒絕了你的 API 金鑰」。無介面的 SSH/TUI 工作階段不會收到 GUI 警示;它們依靠記錄行與改寫過的 npm 錯誤。 - 403(套件被 Depi 阻擋,或金鑰未獲授權存取它)。 記錄並原封不動地放行,因此 npm 會回報 Depi 自身的拒絕文字。
Depi 僅影響 npm——其他七個生態系不受它影響。Depi 在單選鈕中取代的是 socket.dev,而非你的本機政策:時效閘門、OSV 檢查與指令碼剝除仍會對 Depi 所提供的內容執行。
安裝指令碼剝除
npm 的 preinstall、install、postinstall 與 prepare 掛鉤會在安裝時執行任意程式碼,是現實世界 npm 惡意程式的主力。啟用即時從 npm tar 封存檔剝除 preinstall/install/postinstall/prepare(在安裝指令碼群組中)後,代理伺服器會即時改寫每一個 npm tar 封存檔:它會解壓縮 .tgz、定位頂層的 package/package.json、移除那四個指令碼鍵、重新計算 tar 標頭的檢查碼,然後重新以 gzip 壓縮。
由於 tar 封存檔的位元組改變了,代理伺服器也會從套件的登錄表中繼資料中清除 dist.integrity 與 dist.shasum,因此 npm 會自行從剝除後的 tar 封存檔計算雜湊,其驗證在未釘選的安裝中仍然通過。每一次剝除都會被記錄(「stripped install scripts from」該套件與版本,以橘色顯示);經檢查後判定乾淨的 tar 封存檔僅會被標上 X-Bromure-Rewritten: supply-chain 標頭。在任何剖析失敗時,原始的 tar 封存檔會原封不動地放行——此層採故障開放,因為它絕不能弄壞一個格式正確的安裝。
有些套件確實需要安裝指令碼——原生繫結編譯器,例如 better-sqlite3 或 node-canvas。將這些加入允許安裝指令碼的對象(格式為 npm:better-sqlite3),它們就會保留其掛鉤。
附註: 這個代理伺服器層與 VM 內 npm 本身的安裝指令碼政策是分開的。較新的 npm 版本只會為
allow-scripts清單上的套件執行安裝指令碼,因此 Bromure 在每台機器中管理的~/.npmrc只為它隨附的代理程式 CLI 允許執行——@anthropic-ai/claude-code、@openai/codex、@moonshot-ai/kimi-code與@oh-my-pi/pi-coding-agent——讓以npm install -g自我更新的代理程式能取得其原生二進位檔。其他所有套件都遵循 npm 的預設行為。如果你在這裡開啟指令碼剝除,也請把這些套件加入允許執行安裝指令碼的對象,否則代理程式的自我更新會失去安裝步驟。
此切換預設關閉,且有兩個限制適用:
- 僅限 npm。 PyPI 的 sdist 不會被改寫——
setup.py就是任意程式碼,因此在那裡進行剝除不可行。 - 僅限未釘選的安裝。 一個完整性雜湊已在
package-lock.json中釘選的 tar 封存檔,無法在不使驗證失敗的情況下被改寫。那種情況由下一層來管理。
鎖定檔釘選的安裝
一次鎖定檔釘選的安裝——npm ci——會在鎖定檔中釘選每一個 tar 封存檔的完整性雜湊。Bromure 無法在不破壞雜湊驗證的情況下改寫那些 tar 封存檔,因此它唯一的選項是原封不動地放行它們,或阻擋它們。預設情況下它們會靜默放行。
如果你想有所置喙,在鎖定檔釘選的安裝群組中啟用在原封不動放行鎖定檔釘選的 tar 封存檔前提示(npm ci、pip --require-hashes)。一批當中第一個鎖定檔釘選的抓取(對 npm 而言,透過 npm-command: ci 請求標頭偵測)接著會彈出一個主機同意對話框,標題為放行來自工作區「…」的 npm ci(鎖定檔釘選的安裝)?,帶有按鈕允許 15 分鐘、允許一次、在本工作階段其餘時間允許與不允許。整批同時進行的抓取會合併到那一個提示上並遵循你的決定;拒絕會以 451 阻擋該抓取,而該拒絕會被記住 60 秒,好讓重試不會重新提示。
附註: 儘管 UI 標籤提到了
pip --require-hashes,目前偵測僅針對 npm 的npm-command: ci標頭實作——pip 的雜湊釘選安裝不會觸發此提示。(它們也永遠不會被改寫,因為 PyPI 成品永遠不會被修改。)
信任被封鎖的套件
信譽判定不一定適合你:socket.dev 因 workerd 的安裝指令碼而標記它,但它是 Cloudflare 的執行環境,你的專案可能需要它。OSV 或 socket.dev 封鎖套件下載時,不會默默回傳 451,而是保留該下載並詢問:儘管有供應鏈警告,仍要從工作區「…」安裝 … 嗎?,並引述原因。選項如下:
| 選項 | 效果 |
|---|---|
| 在本次工作階段中允許 … | 安裝它,以及該套件的任何其他版本,直到應用程式結束。 |
| 在此工作區中一律允許 … | 安裝它,並將它加入工作區的受信任的套件清單,之後的下載便會略過對它的 OSV 與 socket.dev 檢查——只要它還在清單上。 |
| 不允許 …,並且本次工作階段中不再詢問 | 維持 451;在應用程式結束前,同一安裝會持續被封鎖,不再詢問。 |
| 暫時不要 | 暫時維持 451;一分鐘後會再次詢問。未回應即視同此選項。 |
socket.dev 的惡意軟體判定(惡意軟體、已知惡意軟體、GPT 偵測到的惡意軟體、惡作劇套件、遭入侵的 SSH 金鑰)永遠不提供覆寫選項,也永遠不受清單涵蓋:這些下載一律封鎖。時效閘門保有自己的豁免套件清單,安裝指令碼剝除則有自己的允許執行安裝指令碼的對象清單——在這裡信任某個套件,並不會讓它免除這兩者中的任何一項。
你也可以事後再加入套件:安全性時間軸中被封鎖的列提供 允許… → 在此工作區中一律允許 …,清單本身則位於供應鏈窗格的受信任的套件下(格式 npm:workerd)。變更會立即套用到執行中的機器。
同意提示與授予
所有供應鏈詢問使用者的路徑——鎖定檔直接放行,以及下文所述的未經驗證套件保留——都會經過一個共用的同意仲介,並具備突發合併:對同一範圍的並行請求會等待在單一對話框上,而非堆疊警示。鎖定檔直接放行提供以下四個決定(未經驗證套件的保留有自己的選項,逐一套件——請參閱離線與降級行為):
| 決定 | 效果 |
|---|---|
| 不允許 | 以 451 阻擋;記住 60 秒,自動拒絕立即的重試。 |
| 允許一次 | 讓這一個請求(或合併的突發)通過。 |
| 允許 15 分鐘 | 對該範圍授予 15 分鐘。 |
| 在本工作階段其餘時間允許 | 對該範圍授予直到應用程式結束。 |
作用中的授予與拒絕會在核准 UI(視窗 → 憑證核准…區域)中列為供應鏈決定——與防護欄決定分開——你可以在其中提早撤銷一項授予。授予僅存在於記憶體中,不會在應用程式重新啟動後留存。
在遠端 SSH/CLI 工作階段中沒有 GUI 對話方塊:同樣的問題會以選擇器的形式呈現在機器的 tmux 內。不回應即代表拒絕。請參閱遠端存取。
設定政策
供應鏈政策以機器為單位設定,位於機器設定中安全性底下的供應鏈窗格。完整的逐欄位參考請見供應鏈設定;預設值一覽:
| 設定 | 預設 |
|---|---|
| 拒絕比截止時間更年輕的套件(時效閘門) | 開,**最小時效:**2 天,無豁免 |
| 在 api.osv.dev 上查詢套件(免費、不需金鑰) | 關;**在此嚴重度阻擋:**高度以上 |
| 套件過濾 | 無(無 socket.dev 或 Depi 金鑰) |
| 阻擋已遭入侵的套件 / 阻擋帶有已知 CVE 的套件(socket.dev) | 關;**CVE 阻擋門檻:**高度以上 |
| 即時從 npm tar 封存檔剝除 preinstall/install/postinstall/prepare | 關,空的允許清單 |
| 在原封不動放行鎖定檔釘選的 tar 封存檔前提示 | 關(靜默放行) |
三個操作細節:
-
編輯即時生效。 儲存設定會立即將新政策推送到執行中的工作階段——代理伺服器會針對每個請求讀取政策,因此永遠不需要重新啟動 VM。每一項新的或變更的政策都會在應用程式日誌中以一行摘要確認,例如:
[supply-chain] policy engaged for 1a2b3c4d: age-gate=2d osv=high socket.dev=compromised+cve=high strip-scripts設定錯誤會就在那行摘要中被點名:
socket.dev=key-set-but-no-toggle表示已輸入金鑰但沒有啟用任一阻擋,而depi=selected-but-no-key表示已選擇 Depi 但因缺少金鑰而關閉。 -
儲存位置。 政策存放在工作區位於
~/Library/Application Support/BromureAC/profiles/<id>/下的profile.json中(僅寫入非預設欄位)。socket.dev 與 Depi 的 API 金鑰也存放於此——僅在主機端,在 UI 中顯示為安全欄位,且永遠不會複製到 VM 中。沒有供應鏈專屬的環境變數或啟動引數。 -
遠端設定。 SSH/TUI 遠端選單提供相同的供應鏈窗格與相同的欄位,因此無介面的執行個體可以在沒有 GUI 的情況下設定。見遠端存取。
在安全性時間軸中觀察安裝
供應鏈管線所做的每一項判定都可在安全性時間軸中看到——開啟視窗 → 安全性…,或以 ⌘K 搜尋它。其總覽分頁上的已檢查的套件磚塊會計算過去 24 小時的套件抓取次數;點選它,或在時間軸分頁的引擎篩選中挑選供應鏈,即可逐列查看。
每一列會在其條件欄中列出套件(npm · [email protected]),並在其判定欄中以顏色標示發生了什麼事:
| 判定 | 顏色 | 意義 |
|---|---|---|
| 已檢查 | 綠色 | 已抓取套件的版本清單,且通過檢查。 |
| 已下載 | 綠色 | 已抓取套件成品,且通過檢查。 |
| 已檢查——比冷卻期更新的版本已隱藏 | 藍色 | 時效閘門改寫了版本清單。這是閘門在做它的日常工作,並非阻擋。 |
| 已剝除安裝指令碼 | 藍色 | 已從 npm tarball 移除安裝掛鉤。 |
| 已封鎖——… | 紅色 | 抓取以 451 被拒絕;原因接在破折號之後(例如已封鎖——已知惡意軟體)。你可以覆寫的封鎖會在該列末端有一個**允許…**按鈕。 |
| 已下載 — 儘管有以下警告,你仍允許了:… | 藍色 | 被 OSV 或 socket.dev 標記、但因你允許而安裝的套件(透過提示,或受信任的套件)。 |
無論是否開啟任何供應鏈層,套件抓取一律會被記錄——強制執行依循政策,記錄則不受政策影響。apt 安裝也會被記錄:每個從 Ubuntu 或 Debian 封存檔 pool 抓取的 .deb 都會以 apt 列出現。這些只會被觀察;本章的任何檢查都不適用於它們。
想實際看看:開啟時間軸,在工作階段中執行任一安裝(npm install、pip install、cargo add、gem install、apt install 等),然後看著各列陸續出現。事件會在磁碟上保留 90 天,並在應用程式重新啟動後依然存在;清除只會把目前的列從視窗中隱藏。**輸出…**會把分頁所顯示的內容存成 CSV 檔案。
附註: 時間軸記錄的是判定,而非每一個步驟。詳細的管線記錄行——每一次外送查詢、「policy engaged」摘要、Depi 重新導向、API 錯誤——會寫入位於
~/Library/Logs/BromureAC/bromure-ac.log的應用程式日誌(從終端機啟動時也會寫入應用程式的 stderr)。
離線與降級行為
管線的信譽檢查依賴於你的 Mac 發出的外送呼叫(絕不會從 VM 發出):OSV 用的 api.osv.dev、socket.dev 用的 api.socket.dev、PyPI 發布時間後援用的 pypi.org,以及 Depi 用的 depi-npm-proxy.landh.tech。當一個啟用的來源無法產生判決時——重試後網路仍中斷、HTTP 錯誤、速率限制、驗證失敗,或不支援的生態系——Bromure 會故障關閉,而非靜默允許。
下載會暫停,而一個未經驗證套件的保留會以標題為要在不檢查信譽的情況下從工作區「…」安裝 套件 版本 嗎?的面板詢問你。面板會說明為何沒有判定——socket.dev 沒有回應、拒絕了 API 金鑰(請在工作區的供應鏈設定中檢查)、正在限制該金鑰的請求速率、尚未完成該版本的分析,或不認得該版本(它可能剛發布)——並說明安裝它代表接受一個未對照你所設定的信譽來源檢查過的套件。選項如下:
- 允許 套件 版本 ——安裝此版本,本次工作階段剩餘時間不會再詢問。
- 15 分鐘內允許所有未核驗的套件 ——此套件,以及工作區在接下來 15 分鐘內所有得不到判定的其他套件。
- 本次工作階段剩餘時間不允許未核驗的套件 ——此套件及之後的每一個都會被拒絕,不再詢問。
- 不允許 套件 版本 ——拒絕(以 451)。倒數結束前未作答、按 Esc 或關閉面板,效果相同。
每個工作區同一時間只會顯示一個問題:當 uv sync 或 npm install 遇到好幾個未經驗證的套件時,會逐一詢問(面板會顯示還有幾個在等待),而針對全部的回答會一次解決其餘的套件。
這是刻意的防護:沒有它,一個能對信譽服務誘發速率限制的攻擊者就能讓套件未經檢查地偷渡過去。實際的後果:
- 在完全離線的情況下工作且啟用 OSV 或 socket.dev,意味著每一個未快取的套件都會提示。在離線期間停用那些層——時效閘門能在沒有連線的情況下,從已快取的中繼資料持續運作。
- socket.dev 加上 Cargo意味著每一個 crate 都會提示,因為 socket.dev 完全無法審查 Cargo(授予是以每個 package@version 為單位,因此一次工作階段授予只涵蓋那一個 crate 版本)。
- **PyPI 發布時間後援是唯一的例外:**在純粹的網路錯誤時,它會對該次抓取故障開放(該失敗不會被快取,因此下次會重試該查詢)。時效閘門的中繼資料改寫不受影響。
入侵偵測
供應鏈檢查降低了敵意程式碼進入沙盒的機率;入侵偵測則捕捉那些確實進入的敵意程式碼試圖動作的那一刻。它是憑證系統的一部分,而非套件管線——誘餌憑證與權杖交換的完整全貌在憑證中——但它的警報記載於此,因為一個被下毒的套件是它最可能的觸發因素。
VM 發出的每一個外送請求都會在單一次通行中被掃描,以尋找憑證交換層所鑄造的誘餌(「假」)權杖。一個範圍限定於某個主機(比如 github.com)的假權杖,出現在一個送往任何其他主機的請求中,就是憑證外洩的特徵。當這種情況發生時:
- 代理伺服器以 451 阻擋該請求——目的地永遠收不到一個位元組。
- VM 會立即暫停。如果機器原本在背景執行,會先被帶到螢幕上。凍結的畫面會染成紅色。
- 會出現一則標題為此環境可能已遭入侵的嚴重警示,說明 Bromure 偵測到有人試圖從所指名的機器,將工作階段憑證外洩到非其簽發對象的主機,且 VM 已被暫停——並為每一次外洩列出一行詳細資料:權杖預覽(例如
sk-a…f9q3)、憑證名稱、其簽發對象主機,以及觀察到它正被送往的主機。
此警示提供四種回應:
- 關機(預設)——停止 VM 並將機器標記為已遭入侵。
- 封鎖——只攔下這一個請求並恢復 VM;若代理程式再次嘗試,偵測器會再次觸發。
- 允許——在本次工作階段的剩餘時間內信任所觀察到的主機:憑證會被替換並送往該處,對它的請求也不再被標記。只對你認得的目的地使用。
- 儲存以供調查——你選擇一個檔案夾;Bromure 會匯出
disk.img(VM 系統磁碟的副本)、home.tar.gz(機器的家目錄),以及每個共享檔案夾的shares/<name>.tar.gz,然後關機並將機器標記為已遭入侵。RAM 狀態會被捨棄。
只有關機會回應 Return 鍵;其他三項都需要明確點選。鏡像該機器的遠端用戶端會收到關機、封鎖與允許。完整說明,包括允許背後的每工作階段允許清單,請見憑證。
被標記為已遭入侵的機器在你明確抹除它之前會拒絕再次開機,而抹除流程會警告共享檔案夾不會被抹除——它們可能仍含有受污染的檔案,因此請手動檢查。
對於宣告了主機範圍的憑證,偵測永遠開啟。沒有釘選主機的手動憑證(「任何主機」)永遠無法觸發它,而 Claude/Codex 權杖則使用放寬的同一註冊網域比對(為 api.anthropic.com 簽發的權杖出現在另一個 anthropic.com 主機上時不會發出警報)。一次只會顯示一則警示;警示開啟期間的重複事件會被捨棄。機器可以在其防護欄窗格中以憑證外洩時不發出警示抑制警示與暫停;外洩仍會被封鎖,並仍以紅色的憑證中介列出現在安全性時間軸中(見應急做法)。
企業可見性
對於位在已向 bromure.io 註冊之 Mac 上的機器,每一次中繼資料與成品抓取也會向企業事件串流發出一個 supply_chain.fetch 事件,攜帶生態系、套件、版本、請求種類、結果(allowed、rewritten、blocked 或 stripped),以及原因種類(age_gate、osv、socket_compromised、socket_cve、verify_unavailable、lockfile_denied、scripts_stripped)。光是註冊就能讓管理員擁有全組織的套件下載可見性——一份每個代理程式所安裝的一切的清單——即使在關閉了每一個強制執行層的機器上也是如此。
註冊如何運作,以及管理員在另一端看到什麼,涵蓋於企業中。