ZCode 在未經許可下將完整的 Git 歷史記錄上傳至阿里雲 OSS

ZCode 在未經許可下將完整的 Git 歷史記錄上傳至雲端

重點摘要: ZCode(智譜官方的 AI 編碼桌面應用)會自動封裝您的整個工作區——包括完整的 .git 歷史記錄、LFS 快取、reflogs 以及全域應用程式設定——並使用伺服器提供的 RSA 公鑰進行加密,只要您處於登入狀態,就會將密文直接上傳至阿里雲 OSS。此上傳流程是無條件執行的,無法透過 UI 開關關閉,且解密金鑰僅存在於智譜的後端,這意味著您無法在本地解密這些資料。


起點:一個卡在 pending 狀態的 313 MB 加密封存檔

  • ~/.zcode 目錄(ZCode 的資料根目錄)佔用了超過 700 MB 的空間。
  • ~/.zcode/v2/checkpoints/ 中發現了一個 313 MB 的 baseline.enc 檔案,隨附的元資料顯示它包含了一個 345 MB 的工作區快照,並且已經歷了 564 次上傳失敗。
  • 該快照代表一個商業專案,其完整儲存庫大小為 10 GB;在排除 node_modules 後,封裝後的負載為 345 MB,也就是說,幾乎全是智慧財產權內容。

上傳流程:客戶端封裝 → 直接 OSS POST

app.asar 進行逆向工程後,揭示了一個兩階段的管道:

  1. 憑證請求 – 客戶端向 https://zcode.z.ai/api/v1/snapshot/upload-credential 發送 POST 請求。伺服器回傳:
    • 快照 ID,
    • RSA 公鑰(用於封裝加密),
    • 最大大小限制,
    • 阿里雲 OSS 表單憑證(policyx-oss-signature),以及
    • 動態物件鍵值。
  2. 直接上傳 – 客戶端建立一個 tar.gz 封存檔,使用 AES-256-CTR 加密,並使用提供的公鑰透過 RSA-OAEP-SHA256 包裝對稱金鑰,然後將產生的 *.enc 檔案直接 POST 到阿里雲 OSS。隨後 OSS 會回呼智譜的後端以確認收到檔案。

網路追蹤顯示,應用程式會與 zcode.z.ai 以及兩個阿里雲 OSS 節點保持持續的 HTTPS 連線。

加密金鑰僅屬於伺服器端

其實作遵循教科書式的封裝加密(Envelope Encryption):

keyId: "<version>",
keyWrapAlgorithm: "rsa-oaep-sha256",
publicKeySpkiPem: "<server‑provided PEM>"
  • 內容在本地使用臨時的 AES-256-CTR 金鑰進行加密。
  • AES 金鑰使用伺服器在憑證請求期間提供的 RSA 公鑰進行包裝。
  • 對應的私鑰從未出現在客戶端機器上;嘗試在本地解開金鑰會失敗。

後果:

  • 儲存在磁碟上的 313 MB 密文無法由使用者或 ZCode 客戶端解密。
  • 只有智譜的後端可以解密該快照,這實際上賦予了該服務對整個儲存庫歷史記錄的無限制讀取權限。

實際被上傳的內容:86 % 為 .git 資料

封裝期間產生的清單(以純文字儲存)列出了 42,411 個檔案。大小分佈如下:

路徑 大小 佔負載比例 重要內容
.git/lfs/ 196.1 MB 56.8 % 所有下載過的二進位大型資產
.git/objects/ 102.2 MB 29.6 % 完整的提交物件儲存庫(提交、樹狀結構、二進位物件)
.git/logs/ 0.6 MB 0.2 % Reflogs,本地分支歷史記錄
原始碼與文件 ~46.2 MB 13.4 % src/,設定檔,文件

因此,86.6 % 的上傳負載是原始的 Git 資料,暴露了:

  • 歷史 API 金鑰和後來已刪除的機密資訊。
  • 未推送的分支名稱,揭露了尚未發布的功能。
  • .git/config 條目,包含內部主機名稱和儲存庫 URL。

另一個清單(repo_snapshot_extra_manifest)會對全域 ZCode 設定檔進行雜湊處理,並將其與每個快照捆綁在一起。

UI 開關無法停止擷取行為

UI 中出現了兩個設定:

設定 預期效果 實際效果
優化體驗 (optimizeAgentExperienceEnabled) 抑制遙測 / 模型訓練資料 僅停用發送用於模型訓練的資料;快照擷取仍會執行
儲存庫快照索引 (repoSnapshotIndexingEnabled) 停用快照功能 僅停止伺服器端對已上傳快照的索引;本地封裝與上傳持續進行

程式碼檢查顯示,快照側邊程序在啟動時無條件實例化。唯一的門控條件是來自 tokenProvider 的有效 JWT 權杖。擷取觸發器會在每次 LLM 提示之前 (captureBeforePrompt) 以及標記為 repo-wiki-update 的任務完成時觸發。單次工作階段可能會產生數十次擷取事件。

隱私權政策的遺漏

ZCode 的隱私權政策指出它會收集「對話期間提交的文字、檔案和程式碼」,這對於 LLM 上下文來說是標準做法。然而,該政策、常見問題解答(FAQ)和更新日誌中完全沒有提到自動全工作區封裝或 Git 歷史記錄外洩。唯一相關的條款是一則通用的說明,稱「優化計畫預設為關閉,未經同意不會將輸入用於訓練」。

防禦措施:鎖定 checkpoints 目錄

刪除待處理的封存檔只會觸發新的擷取(重試計數器會增加)。可靠的緩解方法是將 ~/.zcode/v2/checkpoints 目錄設為不可變,防止客戶端寫入新的封存檔。

macOS

rm -rf ~/.zcode/v2/checkpoints
mkdir -p ~/.zcode/v2/checkpoints
chflags uchg ~/.zcode/v2/checkpoints   # 設定不可變旗標
# 驗證:touch 指令應失敗並顯示 "Operation not permitted"

Linux

rm -rf ~/.zcode/v2/checkpoints
mkdir -p ~/.zcode/v2/checkpoints
sudo chattr +i ~/.zcode/v2/checkpoints   # 設定不可變屬性
# 驗證:touch 指令應失敗並顯示 "Operation not permitted"

影響: 當擷取管道無法寫入封存檔時會中止,因此不會上傳任何資料。代價是會失去 ZCode 的「快照還原 / 時間軸」UI 功能;正常的聊天、自動完成和工具執行功能保持正常。若要恢復,請移除不可變旗標(macOS 上使用 chflags nouchg,Linux 上使用 chattr -i)。


社群反應與官方回應

  • Hacker News 的評論強調了對其他 AI 代理讀取點檔案(dotfiles)的擔憂、沙盒化的徒勞,以及對中國 AI 供應商的不信任。
  • 智譜的聲明(評論中連結了截圖)聲稱該行為源於早期發布的「程式碼庫索引」功能,旨在進行本地儲存庫索引和臨時 Wiki 生成。聲明斷言上傳的資料在 Wiki 生成後會立即銷毀,並表示問題已修復,同時向使用者道歉並重置了一週的配額。
  • 批評者指出聲明(臨時、銷毀資料)與技術證據(持續加密上傳至 OSS 且金鑰由伺服器持有)之間存在矛盾。

總結

ZCode 的背景程序會擷取登入使用者的整個工作區——包括完整的 Git 歷史記錄——並在沒有任何使用者可控選項的情況下,將其加密上傳至第三方雲端。加密金鑰僅存在於智譜的伺服器上,這意味著該服務可以隨意解密這些資料。無法接受此類資料收集程度的使用者應鎖定 ZCode 的 checkpoints 目錄(或解除安裝該應用程式)以防止進一步的外洩。

Sources

相關