符號連結走出了工作區,卻一無所獲
GhostApproval 展示了一個惡意儲存庫如何誘騙六款 AI 編碼助理,在它們本該待著的資料夾之外寫入與讀取檔案:在開發者自己的機器上植入 SSH 金鑰、劫持 shell 啟動流程、讀走雲端憑證。這個 bug 是一個跟隨符號連結的錯誤。它之所以造成傷害,是因為代理跑在一台裝滿你祕密的機器上。Bromure Agentic Coding 改變的正是後面這個事實。
編碼代理是一支你邀請來讀寫你筆電上檔案的程式。GhostApproval 講的是:當一個惡意儲存庫說服這支程式往界線外多寫那麼一點—— 寫進你的 SSH 金鑰、你的 shell 設定、你的雲端憑證——會發生 什麼事。這個 bug 又老又乏味。傷害來自代理所站的位置。
2026 年 7 月 8 日,Wiz 的研究團隊發表了 GhostApproval, 記述同一個缺陷以六種不同形式出現在六款熱門 AI 編碼助理中: Amazon Q Developer、Anthropic 的 Claude Code、Augment、Cursor、 Google 的 Antigravity,以及 Windsurf。細節因產品而異,形狀卻 如出一轍。你打開一個儲存庫——也許是某人傳來的教學,或同事 請你看看的專案——把你的編碼代理指向它。而在那個儲存庫的某個 角落,有一個不是檔案的檔案。
一個不是檔案的檔案。
這個把戲是符號連結(symbolic link,symlink):一個看起來像 檔案的檔案系統項目,實際上是指向另一條路徑的路標。符號連結是 再正常不過、有四十年歷史的 Unix 功能。而這個安全問題——編錄 為 CWE-61—— 幾乎一樣老:一支程式若打開符號連結而不檢查它指向哪裡,就等於 讓攻擊者把它引向作者從未打算碰的檔案。
在 GhostApproval 裡,惡意儲存庫附上一個名字看似無害的符號
連結,比如 project_settings.json。它裝的不是設定,而是一個
指向 ~/.ssh/authorized_keys、~/.zshrc 或
~/.aws/credentials 的指標——這些檔案坐在開發者的家目錄裡,
遠在儲存庫資料夾之外。接著,一份 README——或依儲存庫指示
行事的使用者——要求代理「更新 project_settings.json」。代理
跟著路標走,沒有解析它通往何處,於是寫進了真正的目標。
有兩件事讓這不只是個趣聞。第一是這些目標各自的作用。往
~/.ssh/authorized_keys 追加一行,就把攻擊者的金鑰裝進你的
機器,換得免密碼的 SSH 登入。寫入 ~/.zshrc,攻擊者的命令就
會在你每次打開終端機時執行。Augment 的代理甚至不必寫入:被
問到「專案裡的 AWS 金鑰」時,它循著符號連結一路走到憑證檔案,
把祕密直接印進了對話——連一個提示都沒有。
第二是一個更隱微的失誤,Wiz 將它歸類為
CWE-451,
使用者介面誤述。在 Claude Code 上,代理自己的內部推理已經認出
那是個 shell 設定檔,確認對話框卻仍然只問「要對
project_settings.json 進行這個編輯嗎?」人類唯一能攔下危險
的那一刻,看到的卻是錯的資訊。Windsurf 與 Amazon Q Developer
更糟:它們在顯示核准按鈕之前就把檔案寫進了磁碟,於是對話框
成了復原提示,而不是閘門。攻擊者的 SSH 金鑰早已就位。
大多數廠商後來都修補了自家的變體:Cursor 在 v3.0 (CVE-2026-50549)、 Amazon Q 在 language-server 1.69.0 (CVE-2026-12958)、 Google 在 Antigravity v1.19.6,Claude Code 則加上了符號連結 解析警告。這是正確的回應,你也應該裝上這些更新。但逐一修補 每款產品的符號連結處理,修好的是機制,賭注卻留在原地。
賭注多大,取決於代理站在哪裡。
這裡的每一個酬載都建立在同一個假設上:抵達開發者的家目錄是
值得的。~/.ssh 裡有一把能打開真實機器的金鑰,
~/.aws/credentials 裡有一枚有效的雲端權杖,而 ~/.zshrc
會在你明天打開的 shell 上執行。符號連結是路,終點則是一台裝著
你全部工作生活的筆電。
大多數針對編碼代理的攻擊都仰賴同一個假設。代理是一支能力強大、 不設邊界的程式,讀著不受信任的輸入(儲存庫、網頁、套件 tarball、工具輸出),並在一台憑證以明文檔案躺在幾層目錄之外的 機器上採取行動。你儘管加固代理;它和你跑的其他一切共用同一個 檔案系統、同一串鑰匙。攻擊者會不斷鋪出新路。問題在終點。
Bromure Agentic Coding 拿走了終點。代理、它衍生的 shell、它 clone 的儲存庫,以及其中的任何程式碼,全都跑在一台依設定檔 區隔的 Linux VM 裡:一台由 hypervisor 與你的 Mac 隔開的獨立 機器。你真正的憑證從不進入其中。我們在 為什麼 Bromure Agentic Coding 不是沙箱 裡寫過為什麼一道邊界勝過一座牢籠;GhostApproval 是這個想法的 一次乾淨檢驗,因為它逃出的正是工作區資料夾——整個產業賴以 維繫的那道小小邊界。
在 VM 裡把同一場逃逸再走一遍。符號連結照樣解析(Bromure 根本
不需要偵測或阻擋這個把戲),寫入也照樣落在
~/.ssh/authorized_keys 上。但那個檔案住在客體機的家目錄
裡,在一台沒有任何路徑通往你 Mac 的可拋棄機器上。攻擊者的
金鑰如今換來的,是登入一台你即將丟棄的機器——一台打從一開始
就無法從外部連上的機器。
竊取憑證的變體收場更是平淡。當一個被入侵的代理循著符號連結
走到 ~/.aws/credentials、把內容印進對話時,它讀到的是一個
樁:一份格式無誤、金鑰卻無法向任何地方驗證的憑證檔案。
真正的權杖留在主機上,由一個仲介看管——它只在你允許的對外
呼叫發出的那一刻,才把占位符換成真祕密,再從回應裡把它換
回來。一個掃遍檔案系統找祕密的依賴套件或被誘騙的代理,找到的
只是擺設。
那個提示,以及第二張網。
Bromure 在不同的層面上迎擊 GhostApproval 的兩道利刃。
第一道是確認提示的問題,CWE-451:對話框說的是
project_settings.json,代理心裡明白指的卻是 ~/.zshrc。
Bromure 直接拿掉了檔案寫入對話框;發生在可拋棄 VM 裡的寫入,
碰的是一顆可消耗的磁碟,不需要核准。Bromure 把人放回真正的
邊界上,攔的是那些會離開 VM、改變狀態的呼叫:一次
git push、一句資料庫 DROP、一個雲端 Terminate。它們會在
主機上暫停,讓你看到字面上的操作本身,而不是代理寫的摘要。
這正是 GhostApproval 違背的那條原則,只是用在了真正要緊的
地方:一個決定將在沙箱之外產生後果的那一刻,就是人類該看到
真實呼叫的那一刻。
第二道利刃是啟動整場攻擊的那條指令:一份 README 要代理
「更新 project_settings.json」,模型把它當成脈絡讀進去,又
當成命令執行。Bromure 會先在裝置上讀過那些不受信任的內容:
一個本機分類器替傳入的檔案、網頁抓取與工具輸出打分,找出那些
根本不該出現在那裡的指令;而代理奉為長期命令的規則檔案,會
受到更嚴格的檢查。分類器會漏掉某些措辭,而且光靠隔離就足以
擊敗這個攻擊;把它當成第一張網底下的第二張網。
這條線救不了你的地方。
隔離是一種特定的形狀,而形狀有它的邊。
設定檔是長壽的,所以持久化會持續存在。
那個 ~/.zshrc 酬載會在該設定檔的 VM 裡下一次打開
shell 時執行。它跑在一台沒有主機金鑰、只有樁憑證的客體機
上——一個空房間裡的 shell——但它確實會跑。如果某項任務
讓你放心不下,就在一個全新的設定檔裡跑它。
你掛載什麼,就暴露什麼。
VM 預設不放你的任何東西。如果你刻意把一個真實的主機目錄 掛載進某個設定檔,或把一枚有效權杖貼進客體機,符號連結 逃逸就搆得到它。這條線把你的祕密擋在外面;把它們放回去, 是只有你能做的選擇。
刻意限縮仲介的範圍。
樁意味著磁碟上的檔案一文不值,但仲介仍為你允許的呼叫握著 真權杖。一個只需要讀取儲存庫的設定檔,不該帶著一枚能發佈 它的權杖。隔離控制住爆炸的範圍;限縮則決定它最大能有多大。
工作區是錯的那道牆。
GhostApproval 骨子裡講的,是整個產業同意把什麼當成邊界的
故事:工作區資料夾——把「代理待在專案目錄裡」當成一項安全
性質,而不是一個任何路標都能繞開的預設值。符號連結本身是廠商
能修、也確實修了的 bug。把邊界畫在資料夾上,你就得永遠證明
沒有任何符號連結、任何 ..、任何巧妙的路徑會離開它。把邊界
畫在 hypervisor 上,這個問題就不再重要——因為代理逃往的
地方,是一台從未放過你任何東西的可拋棄機器。
裝上廠商的修補吧;它們關上的是真實存在的洞。但這個洞之所以 傷人,是因為代理當時就站在你的筆電上,離你的金鑰只有一層 目錄。把代理移出你的筆電,下一次逃逸(一定會有)抵達的,是 一個空房間。Bromure Agentic Coding 免費、 開源,今天就已上線。