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 進行逆向工程後,揭示了一個兩階段的管道:
- 憑證請求 – 客戶端向
https://zcode.z.ai/api/v1/snapshot/upload-credential發送 POST 請求。伺服器回傳:- 快照 ID,
- RSA 公鑰(用於封裝加密),
- 最大大小限制,
- 阿里雲 OSS 表單憑證(
policy、x-oss-signature),以及 - 動態物件鍵值。
- 直接上傳 – 客戶端建立一個
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
相關
- Dispatch
- Dispatch
- Dispatch
- Dispatch
- Dispatch