ZCode揭秘:悄无声息地将你的Git历史上传至云端
摘要
一项调查揭示,Zhipu的AI编程桌面应用ZCode悄无声息地将用户的整个Git工作区历史上传到Aliyun OSS,使用服务器控制的密钥加密,引发了重大的隐私担忧。
暂无内容
查看缓存全文
缓存时间: 2026/09/18 21:23
# 深入ZCode:静默上传你的完整Git历史到云端
来源:https://blog.ferstar.org/en/posts/zcode-silent-workspace-snapshot-upload/
> 我并非英语母语者;本文由AI翻译。
一切始于一次清理磁盘空间的例行检查:`~/.zcode`文件夹竟然占用了超过700MB空间。经过断断续续的深入调查,我确认了一个相当惊人的事实:**只要你处于登录状态,ZCode(智谱官方AI编码桌面应用)就会静默打包你的整个工作区——包含完整的`.git`历史、LFS资源缓存、引用日志和全局应用配置——进行加密后直接上传至阿里云OSS。**
更具讽刺意味的是:**用于加密的RSA公钥由服务器动态下发,而私钥则完全保留在云端。** 你无法解密存放在自己磁盘上的这份数百MB的密文,ZCode客户端本身同样无法解密。
以下是一份完整的调查记录、证据链,以及一行命令永久封禁该功能的方法。
## 起点:卡在待处理队列的313MB归档文件
`~/.zcode`是ZCode的数据根目录。空间占用分析大致如下:
- `cli/`:约257MB(会话数据库、执行日志)
- `computer-use/`:约130MB(内置应用及运行时依赖)
- `v2/checkpoints/`:约303MB(主要嫌疑对象)
在`v2/checkpoints/`目录内,我发现一个313MB的`.enc`加密文件及对应的元数据文件:
```json
{
"workspacePath": "/Users/ferstar/myprojects/",
"lastCompressedSize": {
"encryptedSizeBytes": 313070842,
"workspaceSizeBytes": 345549173
},
"kind": "baseline",
"failureCount": 564
}
```
情况很清晰:
1. 客户端扫描了我的活跃商业项目,排除`node_modules`等目录,将剩余的345MB打包为313MB的加密归档,标记为`baseline`(全量快照);
2. 系统记录了564次失败的上传尝试,文件因此滞留在本地`pending/`目录等待重试。
该代码库总计10GB;排除依赖项后,剩余的345MB几乎全是核心知识产权。
## 去向:从日志到asar逆向分析
日志中未找到明确的上传URL,于是我解压了客户端的`app.asar`文件。重建的上传流程如下:
```sequenceDiagram
participant C as ZCode客户端
participant S as zcode.z.ai
participant O as 阿里云OSS
C->>S: POST /api/v1/snapshot/upload-credential
S-->>C: snapshot_id + RSA公钥 + max_size + OSS表单凭证 + callback
C->>C: tar.gz打包 → AES-256-CTR加密 → RSA-OAEP密钥包装
C->>O: PostObject直接上传tar.gz.enc
O->>S: callback确认接收
```
该流水线分两阶段运行:
1. **向协调器请求凭证**:客户端调用`https://zcode.z.ai`(代码中为`VITE_ZCODE_ENDPOINT_ORIGIN`)。服务器返回OSS表单签名(`policy`、`x-oss-signature`)、动态对象密钥、大小限制以及本次加密使用的RSA公钥;
2. **直接POST至OSS**:在本地打包并流式加密后,客户端**绕过ZCode自身的应用服务器,通过HTTP POST表单直接将`tar.gz.enc`上传至阿里云OSS**。OSS随后回调智谱后端注册快照。
检查活跃套接字证实了这一点:运行中的ZCode进程保持着与`zcode.z.ai` IP端点以及两个阿里云OSS存储节点的持久HTTPS连接。
## 最具讽刺的部分:密钥属于服务器
加密实现采用了教科书式的信封加密:
```javascript
keyId: String(i.encryption.key_version),
keyWrapAlgorithm: "rsa-oaep-sha256",
publicKeySpkiPem: Ylt(i.encryption.public_key)
```
- 内容使用临时对称密钥通过AES-256-CTR加密;
- 对称密钥通过RSA-OAEP-SHA256使用服务器提供的公钥进行包装。
关键点在于那个公钥:**它在凭证协商阶段由服务器下发,对应的私钥从未接触你的设备。** 使用我系统上所有本地私钥尝试解包装密钥均告失败,这符合预期。
换句话说:磁盘上的那份313MB密文,你和客户端都无法打开。只有智谱后端持有解锁密钥。
如果此功能真正用于用户侧的回滚或跨设备同步,密钥应保存在本地(就像Git或Time Machine那样)。**一个只有服务器能使用的密钥,其目的只有一个:确保服务器能随时读取你的代码。**
## 打包内容:近90%是.git目录
尽管密文已锁定,但打包过程中生成的清单(文件清单)以明文形式保存在本地。分析一个包含42,411个文件的快照:
| 内容 | 大小 | 比例 | 包含信息 |
|------|------|------|----------|
| `.git/lfs/` | 196.1 MB | 56.8% | LFS缓存——所有下载过的二进制资源及大型媒体文件 |
| `.git/objects/` | 102.2 MB | 29.6% | 完整的提交历史对象库(提交、树、Blob) |
| `.git/logs/` | 0.6 MB | 0.2% | 引用日志——本地分支历史与未推送的操作轨迹 |
| 源代码与文档 | 约46.2 MB | 13.4% | `src/`、配置文件、内部文档 |
仅`.git`目录就占**86.6%**的有效负载。一旦上传,云端获得的不仅是当前工作树——而是**自代码库创建以来的完整血统**:
- 历史API密钥及后续提交中已删除的敏感配置;
- 未推送的本地分支名(可能暴露未发布功能计划);
- `.git/config`中配置的内部GitLab主机名与仓库路径。
此外,一个名为`repo_snapshot_extra_manifest`的额外清单会哈希你的全局ZCode配置文件(如`settings.behavior.json`),并在每次快照时跨工作区捆绑上传。
## 开关的真相:UI开关无法阻止该行为
自然反应是检查设置选项关闭该功能。我将UI选项与代码库进行了交叉比对:
| 开关 | 你的预期功能 | 实际功能 |
|------|--------------|----------|
| **优化体验**(`optimizeAgentExperienceEnabled`) | 禁用遥测/数据收集 | **仅控制是否授权数据用于模型训练**。快照捕获与上传仍在运行 |
| **仓库快照索引**(`repoSnapshotIndexingEnabled`) | 禁用快照功能 | **仅控制服务器是否索引已上传的快照**。本地打包与上传不受干扰 |
查看宿主组装代码后结论非常明确:捕获/上传侧边程序在启动时**无条件实例化**。用户偏好设置中没有`if`检查门控;唯一要求是`tokenProvider`能返回有效的JWT。
**底线:只要你处于登录状态,这个后台流水线就永久激活,且无任何UI设置可以关闭它。**
捕获触发发生在两点:`captureBeforePrompt`(每次提示前)以及带有`repo-wiki-update`标签的任务完成时。在会话日志中,单个活跃会话曾产生多达62次捕获事件。
## 隐私政策如是说
查阅ZCode的隐私政策,其中明确说明收集“对话中提交的文本、文件和代码”——这是为LLM提供上下文的标准做法。然而,在整份政策、FAQ及更新日志中,**没有任何一处提及静默打包并上传整个工作区和完整Git历史**。最接近的表述是通用的模板声明:“优化程序默认关闭,未经同意输入内容不会用于训练”。
## 防御方案:删除是打地鼠;锁定目录
当我首次发现待处理包时,直接删除了它。半小时内,它重新捕获——新的313MB归档,重试计数器从564跳至565。当上传器发现文件消失,会重新打包一份。手动删除无异于打地鼠。
最简洁高效的解决方案是在文件系统层设置不可变标志,在内核层拒绝写入权限:
### macOS
```bash
# 清空并锁定检查点目录
rm -rf ~/.zcode/v2/checkpoints
mkdir -p ~/.zcode/v2/checkpoints
chflags uchg ~/.zcode/v2/checkpoints
# 验证:应输出"Operation not permitted"
touch ~/.zcode/v2/checkpoints/test
```
### Linux
```bash
# 清空并锁定检查点目录
rm -rf ~/.zcode/v2/checkpoints
mkdir -p ~/.zcode/v2/checkpoints
sudo chattr +i ~/.zcode/v2/checkpoints
# 验证:应输出"Operation not permitted"
touch ~/.zcode/v2/checkpoints/test
```
### 影响与回退
- **结果**:捕获逻辑尝试磁盘I/O时会被内核阻止。没有本地产物,上传流水线无东西可发送;
- **代价**:“检查点回滚/时间线”UI功能将失效(该功能本就需先上传代码)。普通聊天、自动补全及工具执行不受影响。日志中被吞没的I/O错误无害;
- **恢复方法**:运行`chflags nouchg ~/.zcode/v2/checkpoints`(macOS)或`sudo chattr -i ~/.zcode/v2/checkpoints`(Linux)。
## 结语
使用AI工具时,模型推理必然需要代码上下文——这是大家公认的前提。但此行为在两方面明显越界:
首先,**数据范围**。推理发送任务相关上下文;而快照窃取整个代码库及多年的Git提交历史。
其次,**架构姿态**。若真为用户侧恢复或同步设计,解密密钥应归用户所有。一个服务器独占的加密密钥、隐私政策中的零披露、无法阻止的后台上传、删除后的顽固重打包——这更像数据收集而非备份。
工具终归是工具,但用户必须划定自己的边界。如果软件不允许你关闭它,就用操作系统内核将其锁进牢笼。
相似文章
ZCode 据称被揭露上传工作区/.git 记录到云端。
ZCode 据称被指控静默上传工作区 .git 记录到云端,引发开发者的隐私和安全担忧。
ZCode,GLM编码代理,悄无声息地上传你的Git历史记录
ZCode,一个基于GLM的编码代理,悄无声息地上传用户的Git历史记录,引发对AI开发工具中隐私和安全的担忧。
@fankaishuoai: Zcode 做了个大动作——完整打包并上传了用户的整个项目目录,包括 .git 目录…
Zcode AI 代理上传了用户的整个项目目录,包括 .git,引发了隐私担忧。作者批评了闭源代理,并推荐开源替代方案。
@TheAhmadOsman: "xAI 的 Grok Build CLI 正在将整个 Git 仓库上传到 Google Cloud 存储桶" 这又是一个转向开源…
发现 xAI 的 Grok Build CLI 在未适当披露或确认的情况下,将整个 Git 仓库(包括私有代码和密钥)上传到 Google Cloud 存储桶。
Grok Build CLI 会上传你整个仓库——包括完整 git 历史记录和 .env 密钥——到 xAI 的云端,而选择退出机制也无法阻止该行为(基于抓包分析)
一项安全分析发现,Grok Build CLI 工具会上传整个仓库,包括完整的 git 历史记录和 .env 密钥,到 xAI 的云端,并且提供的退出选择机制并未实际阻止该行为。