返回 AI 情报
技巧精选 772026-09-18 22:22csmantleHacker News 热门(buzzing.cc 中文翻译)

ZCode 被曝登录后会打包上传完整 Git 历史

ZCode 内部揭秘:将您的 Git 历史记录静默上传至云端

精选理由

作者通过逆向与实测给出完整证据链和可复用的目录锁死命令,读者可据此核查并阻断 ZCode 的静默上传行为。

AI 摘要

作者称,智谱 AI 编程应用 ZCode 在登录后会将工作区打包加密并上传至阿里云 OSS,内容包括完整 Git 历史、LFS 缓存和全局配置。调查记录中的单个加密快照为 313MB;私钥只存于云端,用户及客户端无法解密本地密文。

正文 · AI 翻译

我不是英语母语者;这篇文章由 AI 翻译。

事情始于一次清理磁盘空间时的例行检查:~/.zcode 占用了超过 700MB。在断断续续地深入排查后,我确认了一件相当离谱的事:

只要你处于登录状态,ZCode(智谱官方的 AI 编程桌面应用)就会悄悄地把你的整个工作区——完整的 .git 历史记录、LFS 资源缓存、reflog 以及全局应用配置——打包、加密,然后直接上传到阿里云 OSS。

更讽刺的是:用于加密的 RSA 公钥是由服务器动态下发的,而私钥只存在于云端。你无法解密就躺在你自己磁盘上的那几百兆密文,ZCode 客户端本身也做不到。

以下是完整的调查记录、证据链,以及一条能永久关闭它的单行防御措施。

起点:一个卡在待处理状态的 313MB 归档文件 #

~/.zcode 是 ZCode 的数据根目录。体积构成大致如下:

cli/:约 257MB(会话数据库、执行日志)

computer-use/:约 130MB(打包的应用及运行时依赖)

v2/checkpoints/:约 303MB(主要嫌疑对象)

在 v2/checkpoints/ 内部,我发现了一个 313MB 的 .enc 文件,旁边还有一个状态元数据文件:

{ "workspacePath": "/Users/ferstar/myprojects/<a commercial project>", "lastCompressedSize": { "encryptedSizeBytes": 313070842, "workspaceSizeBytes": 345549173 }, "kind": "baseline", "failureCount": 564 }

事情的经过很直白:

客户端扫描了我正在进行的商业项目,排除了 node_modules 和其他几个文件,然后把剩余的 345MB 打包成一个 313MB 的加密归档文件,标记为 baseline(完整快照);

它记录了 564 次上传失败尝试,最终让它停留在本地 pending/ 目录中,等待下一次重试。

该仓库总计 10GB;减去依赖项后,剩余的 345MB 几乎全是核心知识产权。

它去了哪里:从日志到 asar 逆向工程 #

日志中没有明确的上传 URL,所以我拆开了客户端的 app.asar。重建后的上传流程如下:

sequenceDiagram participant C as ZCode client participant S as zcode.z.ai participant O as Aliyun OSS C->>S: POST /api/v1/snapshot/upload-credential S-->>C: snapshot_id + RSA public key + max_size + OSS form credentials + callback C->>C: tar.gz pack → AES-256-CTR encrypt → RSA-OAEP wrap key C->>O: PostObject direct upload of tar.gz.enc O->>S: callback confirms receipt

该流水线分两个阶段运行:

向协调器请求凭证:客户端调用 https://zcode.z.ai(代码中为 VITE_ZCODE_ENDPOINT_ORIGIN)。服务器返回 OSS 表单签名(policy、x-oss-signature)、一个动态 Object Key、大小限制,以及本轮加密所用的 RSA 公钥;

直接以表单 POST 上传至 OSS:在本地完成归档和流式加密后,客户端绕过 ZCode 自己的应用服务器,通过 HTTP POST 表单tar.gz.enc直接上传至阿里云 OSS。随后 OSS 回调智谱后端以登记该快照。

检查活动套接字证实了这一点:运行中的 ZCode 进程维持着到 zcode.z.ai IP 端点以及两个阿里云 OSS 存储节点的持久 HTTPS 连接。

最讽刺的部分:密钥属于服务器#

该加密实现采用的是教科书式的信封加密:

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 #

尽管密文是加密锁定的,但打包过程中生成的 Manifest(文件清单)以明文形式保存在本地。拆解一份包含 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% reflog——本地分支历史与未推送的操作痕迹

源代码与文档 约 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) 禁用快照功能 仅控制服务器是否对上传的快照建立索引。本地打包和上传仍不间断进行

查看宿主程序集代码就一目了然:捕获/上传 sidecar 在启动时被无条件实例化。没有任何基于用户偏好的门控if检查;唯一的要求是tokenProvider能够返回一个有效的 JWT。

归根结底:只要你处于登录状态,这条后台流水线就永久处于活跃状态,没有任何 UI 设置能将其关闭。

捕获触发发生在两个时间点:captureBeforePrompt(每次提示词之前)以及任务完成并标记为repo-wiki-update时。在会话日志中,单个活跃会话最多产生了 62 次捕获事件。

隐私政策怎么说 #

查阅 ZCode 的隐私政策,其中明确声明会收集“对话过程中提交的文本、文件和代码”——这是为 LLM 提供上下文的常规做法。

然而,纵观整份政策、FAQ 和更新日志,没有任何一处提及会静默打包并上传整个工作区和完整的 Git 历史记录。

最接近的表述是那条通用模板声明:“优化程序默认关闭,未经同意输入内容不会被用于训练”。

防御:删除是打地鼠;锁定目录 #

当我第一次发现那个待处理的软件包时,我直接把它删掉了。不到半小时,它又重新捕获了——一个全新的 313MB 归档文件,重试计数器从 564 跳到了 565。当上传方发现文件不见了,它就会重新打包一个新的。手动删除就是打地鼠。

最干净、最有效的解决方案是在文件系统层面设置不可变标志,在内核层面拒绝写入权限:

macOS #

# Wipe and lock the checkpoints directory rm -rf ~/.zcode/v2/checkpoints mkdir -p ~/.zcode/v2/checkpoints chflags uchg ~/.zcode/v2/checkpoints # Verify: should output "Operation not permitted" touch ~/.zcode/v2/checkpoints/test

Linux #

# Wipe and lock the checkpoints directory rm -rf ~/.zcode/v2/checkpoints mkdir -p ~/.zcode/v2/checkpoints sudo chattr +i ~/.zcode/v2/checkpoints # Verify: should output "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 提交历史。

第二,架构姿态。如果这真的是为用户侧恢复或同步而设计,解密密钥本应归用户所有。而加密密钥完全由服务器持有、隐私政策中零披露、后台上传无法阻止、删除后仍顽固地重新打包——这看起来远不像备份,更像是数据收集。

工具就是工具,但用户必须自己划定边界。如果软件不让你关掉它,那就用操作系统内核把它锁进笼子里。

相关

原文

Original Title

ZCode 内部揭秘:将您的 Git 历史记录静默上传至云端

Source

Hacker News 热门(buzzing.cc 中文翻译)

Site

blog.ferstar.org

Author

csmantle

Published

2026-09-18 22:22

阅读原文· blog.ferstar.org

继续阅读