返回所有文章
發佈於 · 作者 Renaud Deraison

隔離把那個指令跑了

2026 年 8 月 10 日,Manifold Security 公布了 Cursor CLI 代理的一個缺陷:那個讓代理在隔離的 git worktree 裡啟動的旗標,會從你剛剛複製下來的儲存庫讀一個 JSON 檔,再把內容原封不動交給 shell——發生在 Workspace Trust 詢問之前,而且即使你開了沙箱,它也在沙箱之外。沒有模型,沒有提示注入,沒有勸說。Bromure Agentic Coding 提供同一個 worktree 原語,但儲存庫沒有任何地方可以放進一條指令,而且整件事發生在一台早在儲存庫抵達之前就存在的 VM 裡。

你輸入的那個旗標,意思是「把這個隔離起來」。就是那個旗標,以你的身分、在你的 機器上、在任何東西問你信不信任這個儲存庫之前,執行了儲存庫的 shell 指令。

2026 年 8 月 10 日,Manifold Security 的 Francisco Rosales 公布了 Cursor 命令列編碼代理的一項發現, 三天後 The Hacker News 在 ThreatsDay 匯整 中跟進報導。概念驗證打開的是一台計算機。那是 Manifold 詳列的那份清單的客氣版 本:讀 ~/.ssh、從環境取走雲端憑證、開一個反向 shell、寫下持久化。

請把它當成一種投遞方式來讀,而不是當成一份酬載。沒有人注入任何東西,沒有人勸說 任何模型,沒有哪句聰明的話藏在 README 裡。儲存庫裡一個被追蹤的 JSON 檔指名了一 條指令,而代理裡那個唯一職責就是隔離的部分,把它跑了。

一個旗標、一個 JSON 檔,和錯誤的順序

cursor-agent 是 Cursor 編碼代理的終端機版本。把代理放到你的工作樹上會讓人緊 張,所以這個 CLI 提供了圍堵手段,並寫進它自己的參數說明:

-w, --worktree [name]    Start in an isolated git worktree at
     ~/.cursor/worktrees/<reponame>/<name>

全新的 worktree 是一份乾淨的簽出,所以它沒有你的任何建置產物。為了讓它可用, CLI 在建立 worktree 時會預設執行一個設定步驟。那個步驟會從它剛剛簽出的儲存庫裡 讀 .cursor/worktrees.json,並把 setup-worktree 的值直接交給 sh -c

{
  "setup-worktree": "<any shell command>"
}

Manifold 對「那個值和 shell 之間隔著什麼」的描述只有三個詞:「沒有解析、沒有白 名單、沒有詢問。」

.cursor/worktrees.json 是一個普通的被追蹤檔案。它會隨著一次平常的 git clone 一起到來,就像 README 或一個鎖定檔一樣。在 2026.07.23-e383d2b 之前的組建 裡,worktree 的建立與設定發生在工作區解析的過程中——而工作區解析會在那條負責顯 示 Workspace Trust 對話框的程式路徑之前就結束。所以指令先跑。在 Manifold 報告 附上的錄影裡,終端機還在印 Running worktree setup commands…,計算機就打開了, 接著才出現那個問你信不信任這個目錄的對話框。

Cursor 對那個對話框的用途說得很明白:儲存庫能控制的任何東西,都不該在你接受之 前執行。當有東西真的跑了,業界對這類錯誤有個名字——信任前執行——而 Cursor 之前 就在同一個目錄裡修過一次。2025 年,儲存庫附帶的 .cursor/mcp.json 會在你一打 開專案時就自動啟動它所設定的伺服器。那件事變成了 CVE-2025-64109, 評為 High,CVSS 8.8。同一個 CLI 對 .cursor/cli.json 過於寬鬆的處理則變成了 CVE-2025-61592, 一樣是 High,一樣是 8.8。當時 -w 這條 worktree 路徑還不存在。它在那次修補的五 個月後才出貨,帶著同一個原語,而且沒有任何關卡。

Manifold 在 7 月 20 日回報。Cursor 在 7 月 23 日出貨了重新排序:信任對話框現在 會先繪製,設定指令則要等到工作區被信任之後。7 月 29 日,Cursor 以僅供參考 (Informative)結案,理由是利用這個問題需要使用者去複製一個由攻擊者控制的儲存 庫。值得記住的是 Manifold 的回答。複製儲存庫 正是這個產品存在的理由, 而它同樣也是 CVE-2025-64109 的前提條件——那個 Cursor 評為 8.8 並修補了的錯誤。 複製描述的是投遞方式。缺陷在別的地方。

2026.07.23-e383d2b 之前 — cursor-agent -w demogit clone.cursor/worktrees.json隨之而來,被追蹤工作區解析worktree 建好,設定步驟讀取該檔sh -c "<他們的指令>"你的帳號、你的環境,sandbox: insecure_noneWorkspace信任對話框「信任嗎?」對話框出現時,儲存庫的指令早就跑完了。七月修補之後 — 重新排序Workspace信任對話框現在排在最前你按下 [a] Trust為一個你複製下來正要閱讀的儲存庫sh -c "<他們的指令>"仍是你的帳號、你的環境,仍是 insecure_none — --sandbox enabled 到不了這裡同樣三件事換了個順序,而這三件事全由那個本該被它們約束的行程自己決定。
三道控制,全都在它們本該約束的那個行程內部。在 2026.07.23-e383d2b 之前的組建裡,worktree 的設定指令在工作區解析期間執行——早於信任對話框——而且執行時的沙箱政策被釘死在 insecure_none 上,--sandbox enabled 也改不了它。七月的修補把指令移到了信任關卡之後。它仍然是同一個 shell,對同一個使用者帳號有同樣的觸及範圍。

你打開的那個設定,到不了那條路徑

還有第二條腿,而且它在修補後存活了下來。

那個設定步驟是在一個寫死的 insecure_none 沙箱政策底下執行的。那是 Cursor 自己 給這個值取的名字:在 sandbox.json 裡,type 接受 workspace_readwrite(預 設)、workspace_readonlyinsecure_none,最後一個會完全關掉沙箱。worktree 的設定路徑被釘死在最後一個上。傳 --sandbox enabled 也改不了它。Manifold 這樣 描述目前的組建:更新「關上的是信任前的那扇窗,不是沙箱的那個缺口」。

把這兩條腿並排放在一起,你會得到一件比這個產品活得更久的事。信任關卡是一段序列 裡的一步,所以它可能落在錯的位置。沙箱是一個設定,所以某條程式路徑可以讓自己豁 免。worktree 帶著隔離這兩個字,所以使用者從中讀出了圍堵。這三件事都由 Cursor 自 己的程式碼決定,在它自己挑的時機,在它們本該約束的那個行程內部。那正是兩週前 打敗某個函式庫安全旗標的同一種失敗,也是 七次沒有任何東西逃出沙箱的沙箱逃逸背後 的那一種。

一種與你共用帳號的隔離

-w 隔離的是工作樹。它給代理一份自己的簽出,讓它不會踩壞你尚未提交的工作——這 是個真實的問題,也是個真實的解法。沒有人是為了隔離機器才做它的。worktree 住在 ~/.cursor/worktrees/,在你的使用者底下,帶著你的環境、你的 ~/.ssh、你的 shell 設定檔、你的雲端憑證,而你的鑰匙圈就在一次系統呼叫之外。Manifold 列出的 「設定指令本來可以做什麼」,讀起來就像那個家目錄的財產清冊。

所以:一個你還沒讀過的儲存庫選了一條指令,而你為了安全才呼叫的那個功能,正是 執行它的那個。一個 JSON 檔和一個 shell,模型從頭到尾坐在場邊。

關於代理安全的討論,大半已經飄向模型。它好不好騙、能不能被說服、有沒有讀到不該 讀的東西。這些問題都重要, 我們也寫了很多篇。與此同時,2026 年要在開發者筆電上執行程式碼,最穩的辦法仍然是把它寫進某個工具啟動時會讀的檔 案,然後等某個人去複製。

一個沒有地方放指令的 worktree

Bromure Agentic Coding 提供同一個原語,因為這個原語是好的。 在儲存庫分頁按 ⇧⌘G,輸入一個任務名稱,挑一個代理,Bromure 就會從目前的提交切出 一條 wt/<slug> 分支,把它簽出到 ~/.bromure/worktrees/<repo>/<slug>,在那裡 開一個分頁,並用你的提示啟動代理。好幾個任務、好幾條分支、好幾個代理,一個儲存 庫——艦隊模式

Bromure 的設定步驟和 Cursor 的工作一樣:乾淨的簽出缺了代理需要的那些被 gitignore 的檔案,所以總得有東西把它們搬過來。差別在於儲存庫被允許就此說些什麼。儲存庫可 以附一個 .worktreeinclude 檔,其中每一行非註解的內容都是相對於主 worktree 根 目錄的路徑。Bromure 用 cp -a 逐一複製,而且只在來源存在、目的地不存在時 才複製。那個檔案裡沒有任何欄位裝得下一條指令,所以沒有需要正確排序的 sh -c, 也沒有可以搞錯的順序問題。一個充滿敵意的 .worktreeinclude 最壞也只能要求把某 個檔案複製進簽出裡。整套詞彙就這麼多。

更大的差別在於這一切發生在哪裡。

你帳號裡的 worktreesetup-worktree → sh -c由儲存庫挑選,以你的身分執行只隔一個目錄的東西~/.ssh/id_ed25519AWS_SECRET_ACCESS_KEY, ANTHROPIC_API_KEY~/.git-credentials, ~/.kube/config~/.zshrc — 持久化對外:一個普通開發工具發出的一個普通連線工作樹隔離了,機器是共用的。Bromure 設定檔裡的 worktree.worktreeinclude → cp -a只有路徑 — 沒有欄位裝得下指令拋棄式 Linux VM · 在複製之前就建好這裡沒有私鑰 — 主機 ssh-agent 走 vsock環境憑證都是誘餌(brm_…、假 kubeconfig)持久化隨任務一起過期對外:在主機代理伺服器上被掃描 —誘餌離開它的範圍 → 上游呼叫中止,回 451 給客體、VM 暫停、紅色警報worktree 隔離工作樹,Hypervisor 隔離機器。
同一個儲存庫、同一個設定步驟、兩台不同的機器。左邊,worktree 是你帳號裡的一個目錄:在那裡跑的東西碰得到你的金鑰、你的權杖、你的 shell 設定檔。右邊,worktree 住在一台早在儲存庫被複製之前就建立好的拋棄式 VM 裡——私鑰從未進去過,手邊搆得到的憑證都是誘餌,而帶著其中一個出門的外送請求,會在目的地看到任何一個位元組之前,就在主機端的代理伺服器上被中止。

把那條指令跑起來。看看它找得到什麼。

拿 Manifold 那份清單,讓它走過一個設定檔,並且把一切都讓給攻擊者——信任之前、沒 有沙箱、你高興的話還可以在客體裡當 root。

~/.ssh 那裡沒有東西可讀。一個設定檔的私鑰留在主機上。VM 拿到的 SSH_AUTH_SOCK 指向 /tmp/bromure-agent.sock,透過 vsock 橋接到你的 Mac 上一 個屬於該設定檔的代理程式,而 Bromure 刻意不去接上你 macOS 的 launchd 代理。代理 協定裡有「列出我的公鑰」和「簽署這個挑戰」這樣的請求。它沒有任何一種請求的意思 是「把私鑰交出來」。打開 Require approval to use,每一次簽章都會變成主機上 的一個對話框,附帶一個有時限的授權——五分鐘、一小時、這場工作階段的其餘時間。

從環境取走雲端憑證。 它們就在那裡,看起來也很對,而它們是假的。設定檔的 Credentials 面板裡的每一項,都是以佔位值的形式注入,再由主機端的代理伺服器在線 路上換成真值:通用 API 金鑰用 brm_…、一份帶著用完即丟客戶端憑證的合成 ~/.kube/config~/.docker/config.json 裡一個假的 base64 區塊、 ~/.git-credentials,以及在主機端重新簽章、對任何想繞過代理伺服器的人回 InvalidSignatureException 的 AWS 材料。竊取會成功,然後 什麼值錢的都沒帶回去

開一個反向 shell。 現在攻擊者得把某樣東西送過一條線,而那條線屬於主機。VM 每一個對外請求,都會用 Aho-Corasick 自動機與這個設定檔鑄出的誘餌逐一比對——標頭 與主體,整個請求,而不只是看起來像憑證的那些部分。一個誘餌前往它被鑄造時的範圍 之外的主機,正是一台機器正在把它根本不該知道的東西帶出去的簽名。代理伺服器會在 目的地看到任何一個位元組之前中止上游呼叫,回一個 451 給客體,暫停 VM,把凍住的 畫面染紅,並發出一則點名該憑證、它原本被鑄給哪台主機、以及它實際去了哪台主機的 警報。由你選擇:關機、留存供調查——磁碟映像、家目錄、共享資料夾,打包並標記起 來,讓這個設定檔在被清除之前不會再開機——或是繼續。

寫下持久化。 這台機器的壽命就是一個任務。Erase home 會重置 /home/ubuntu,Reset to base 會重新複製工作區的系統磁碟。

與此同時,你原本要的工作照樣往前走。代理審視儲存庫、跑測試、開拉取請求。儲存庫 的那條指令執行了——在一個「執行」就是它唯一能做的事的房間裡。

打補丁涵蓋不到的那部分

Manifold 的文章裡還有一個細節,而它的保存期限最長。Cursor 沒有為這條 worktree 路徑發布任何資安公告,也沒把它寫進七月的變更紀錄,所以修補是夾在一次例行組建裡 出貨的。任何在受影響版本上的人,都無從得知更新關掉的是一條信任前執行的路徑。而 你今天也無從得知,你筆電上哪個工具還開著這樣一條路徑,因為那條路徑永遠是某個工 具啟動時會讀、而還沒有人稽核過的某個檔案。

Cursor 三天就把這個關掉了,很快。Cursor 在 2025 年也關掉了 mcp.json 那個版 本,然後五個月後來了一個帶著同一個原語的新功能。把功能一路塞進啟動序列,看起來 就是這樣:每多一種能力就多一步,而每一步都是在某個星期四要多排對一次的一件事。

Hypervisor 不是那個序列裡的一步。它不讀 .cursor/worktrees.json,不讀 .worktreeinclude,也不讀你儲存庫裡的任何東西。它在複製之前就在那裡了,它沒有 一個能讓某條程式路徑釘死成 insecure_none 的政策欄位,而它的保證——這台機器是 拋棄式的、這些憑證是假的、這條線有人在看—— 在某個繞過手法被公開的那天,和它被修補的那天,是一樣的

繼續複製你還沒讀過的儲存庫吧。審視不熟悉的程式碼就是這份工作,把它交給代理則是 養一個代理的意義。只要別再讓這場審視發生在你放 SSH 金鑰的同一個帳號裡就好。 安裝 Bromure Agentic Coding,給每個任務自己的分支、自己的簽 出、自己的拋棄式機器,那麼下一次某條啟動路徑被發現會照著某個 JSON 檔說的話執行 任何東西時——而這一定會發生——它會在一個為它而建的房間裡執行。