作者称,智谱 AI 编程应用 ZCode 在登录后会将工作区打包加密并上传至阿里云 OSS,内容包括完整 Git 历史、LFS 缓存和全局配置。

一个清磁盘清出来的313MB加密包

从700MB本地目录到42411个文件的清单

这件事的起点很朴素:磁盘空间告急,某开发者在清理 ~/.zcode 目录时发现它占了 700 多 MB。往下扒了一层,在 v2/checkpoints/ 底下找到一个 313 MB 的 .enc 加密包。

旁边的 state.json 记录了这次快照的基本信息,以及一个关键状态——lastAcceptedManifestHash 字段为空,意味着这次上传没有被服务端接收,它已经在本地 pending 队列里重试了 564 次。

更关键的是旁边那份明文文件清单。清单里总共 42411 个文件,其中 .git 目录相关内容占了 86.6%,约 271 万字节。这包括 Git 对象数据库、LFS 大文件缓存指针、reflog 历史日志,以及 .git/config 里的内部 GitLab 域名配置。

为什么这份清单比数据本身更让人睡不着

清单里有一些比「代码被上传」更具体的细节。

.git/objects 里存着每一个历史版本的完整内容。你三年前删掉的配置文件、两年前换下来的旧密钥、临时测试用的内网地址,全都以 blob 形式躺在对象数据库里。Git 的设计哲学是「删掉了等于还在」——只要对象还在,就能恢复。这份清单里,这些历史版本全部可还原。

.git/logs/HEAD 记录了 reflog,即你本地所有的 HEAD 移动轨迹。你在本地创建过哪些分支、删过哪些分支、rebase 过哪些提交,都在里面。

.git/config 里有 remote.origin.url,即你配置的远端仓库地址。即使这个仓库从未推送到远端,这个配置项依然记录了你想推到哪里。

清单里还有一类文件值得注意:.env 历史版本、secrets/ 目录下的旧密钥文件、CI/CD 配置文件里硬编码的 Token。这些文件在当前工作区可能被 .gitignore 屏蔽,但在 .git/objects 里,它们的历史版本不会被忽略。

程序员 reaction:柯南00089 找到你了

真相锁定:清单不会说谎

程序员反应图:我可能是个假程序员

##一个清磁盘清出来的313MB

打包的是什么:除了当前代码,还有你删掉的一切

.git里藏着什么:删掉的密钥、没推的分支、内网地址

这是很多开发者最容易忽略的地方。Git 的工作方式是追加式历史记录——你执行 git rm 删掉的配置文件,在当前树里确实消失了,但 .git/objects 里还完整保存着那一刻之前的所有版本。

具体来说,一次完整的 .git 快照可能包含以下内容:pack 文件中压缩存储了仓库创建以来所有提交的对象;reflog 记录了所有 HEAD 操作历史;.git/config 中可能记录了内部 GitLab 域名、SSH key 路径;LFS 大文件缓存目录包含曾经 checkout 过的二进制文件完整副本。

「删掉了」在 Git 的世界里从来不等于消失。它只是不在当前工作树里了。而 ZCode 打包的是整个工作区目录,自然也包括 .git

一位开发者 ferstar 在博客中记录了自己的发现:他拿到的商业项目快照中,.git 占 86.6%。这意味着云端拿到的不只是这个项目现在的代码,而是这个仓库自创建以来的全部历史底稿。

为什么.gitignore保不住你

Git 的机制设计决定了 .gitignore 只管「工作树」,不管「历史」。你 .gitignore 掉的文件,在当前目录不会出现;但曾经提交过、后来又被 ignore 的文件,它的历史版本依然存在于 .git/objects 中。

从取证数据看,ZCode 的打包逻辑是遍历整个工作区目录,而不是走 git archive 之类的 Git 原生接口。打包的范围是由文件系统决定的——只要文件在磁盘上,就会被打包。你 .gitignore 掉的当前文件确实不会进包,但 .git 目录本身没有被 ignore,它的子目录全部会被收进去。

风险分两层:第一层是 .gitignore 能保护的部分——当前被 ignore 的文件不会出现在打包中;第二层是 .gitignore 完全无法触及的部分——所有曾经存在于历史中的文件,无论现在是否被 ignore,都以对象形式保存在 .git 里。

纯本地仓库上传后,世界上多了第二份

这是最容易被低估的风险场景。如果你的仓库始终只有本地一台机器在维护,没有远端 remote——那么在 ZCode 打包上传之前,这个世界上只有你一个人、一台机器拥有这份仓库的完整历史。

打包成功之后,世界上多了第二份。而且这份数据存储在云端的对象存储里,不在你的控制范围内。

一位开发者在知乎上记录了自己的独立取证:他有一个 2.36MB 的快照在 2026 年 9 月 15 日已被服务端成功接受,这个工作区的名字叫 Desktop/Mano_hand,没有任何 remote 配置,全网唯一一份完整历史原本只存在于他的 Mac 上。取证之后,这份历史同时出现在阿里云 OSS 的某个 bucket 里。

程序员 reaction:OTHERSMADE

关键事实:私钥不在你手里

这把钥匙是服务端的,而且是动态下发

双层加密:AES-256-CTR + RSA-OAEP-SHA256

加密包的机制是这样的:客户端在工作区根目录生成一个随机 AES-256 对称密钥,用它对整个工作区做流式加密,输出为一个 .tar.gz.enc 文件。同时,客户端请求服务端获取一个 RSA 公钥——这个公钥不是硬编码在客户端里的,而是每次调用 /api/v1/snapshot/upload-credential 接口时由服务端动态下发。

拿到公钥后,客户端用 RSA-OAEP-SHA256 方案把 AES 对称密钥包一层,然后把这个包装进上传表单里,连同加密后的文件一起 POST 到阿里云 OSS 的预签名地址。

整个过程不经过智谱的业务服务器中转,流量直接从客户端走向 OSS。OSS 上传成功后触发回调,通知后端记录这次快照的元数据。

更准确地说,解密所需的全部要素都在服务端:AES 密钥被 RSA 私钥加密,而 RSA 私钥只存在于智谱的服务器里。ferstar 拿本机所有私钥(包括 GPG、SSH、代码签名证书)去解这个包,全部失败。

这意味着一件事:你本地生成的加密包,连你自己都打不开。

单向性是这个设计最刺眼的地方。如果这是备份或同步工具,用户应该能下载回来;如果这是数据归档,用户应该有校验入口。但取证过程中,开发者在所有客户端代码里只找到了一个上传接口,没有任何 restore、download、verify 接口。

这套上传链路为什么关不掉

两个 UI 开关各管什么:训练优化 vs 快照打包

ZCode 界面里确实有两个开关,但它们的职责划分比看上去要细得多。

第一个是「体验优化计划」,默认关闭,控制的是遥测数据和崩溃报告是否回传。这个开关是真实的,关掉后会停止非功能性的数据收集。

第二个是「仓库快照索引」,默认关闭,声称用于生成代码仓库的可视化索引。但 ferstar 的测试结论是:关掉「仓库快照索引」并不会停止快照打包和上传。原因是客户端代码里,快照打包逻辑和 UI 开关之间没有直接绑定——开关只控制是否调用索引生成逻辑,而打包上传是一个独立触发的后台任务,它的触发条件是「登录状态 + 有活跃工作区」,而非「索引功能已开启」。

双层加密的设计陷阱:私钥只在云端

快照从生成就带着一个结构性不对称。客户端生成随机 AES-256 对称密钥,用它流式加密打包好的 tar.gz 文件。然后把那个对称密钥用 RSA-OAEP-SHA256 包一层——公钥来自服务端下发。

ferstar 拿自己机器上的所有私钥去解那个 313MB 的 .enc 文件,全部失败。客户端本体也无法解密。

这跟普通备份的本质区别是:可用性从落盘那一刻起就不归你控制。你只知道包上传了,但包里到底有什么,只有服务端知道。

300 多次重试失败,数据还在排队

ferstar 观察到的那个 313MB 快照,当时已经重试了 564 次,仍然卡在 pending 状态。

这说明两件事:一是网络或 OSS 端在那段时间存在连接问题,二是即便上传成功了,客户端也没有提供从服务端拉回或核对内容的接口。

V2EX 上有用户提到自己机器上一个 2.36MB 的快照显示了 lastAcceptedManifestHash 非空,证明服务端确实接受过数据。而同一台机器上另一个更大规模的快照却一直在重试队列里打转。

数据要么已经被服务端签收,要么在本地排队等着下一次机会。两种状态同时存在,用户完全无法判断哪一份已经离开了自己的机器。

这也是这套机制最难审计的地方。你看到它在传,但你看不到它传到了哪里,更看不到传了什么。

官方回应和独立取证,哪些地方还没对上

智谱的道歉声明发布于 9 月 18 日 17:44,在 ZCode 官方飞书群里发出,随后晚上正式对外发布公告。声明里有三处关键信息:承认上传了数据,归因是「代码库索引」功能;声称生成 wiki 页面后仓库数据会被销毁,不会保存;表示已修复并将开源,同时补偿额度重置为周末 3 亿 Token 或 Coding Plan 续费。

这三处信息里,第二处最值得追问。「销毁」是单方面声明,缺乏可验证证据。ferstar 在独立复查中指出,ZCode 的上传链路是单向的——没有对应的 restore 或 download 接口。数据一旦上去,云端是否有副本、是否真的销毁,用户无法验证。

智谱道歉声明的三个关键信息

「代码库索引」是内部功能名称,对外解释是「方便用户生成 wiki 页面」。这意味着上传行为的动机并非恶意窃取,而是产品功能设计失误——默认开启、无法关闭、权限失控。

但动机和后果是两回事。即使用途是生成 wiki,打包整个 .git 历史并上传到第三方云存储,仍然是过度采集。业内常见做法是只收集当前工作区文件,或者让用户显式授权后才上传。

补偿方案也值得看。周末 3 亿 Token 对重度用户有意义,但对受影响的企业用户来说,真正的损失不是 Token 额度,而是数据泄露风险。一家公司把包含内部域名、未推送分支名的完整 Git 历史传上了云端,这个风险的评估维度不在 Token 数量里。

ferstar复查与独立复现的结论

ferstar 在 9 月 19 日发布了复查结果。他逆向了 3.14.0 版本的 ZCode 客户端,确认 repoSnapshot 上传管道已被物理剥离,/api/v1/snapshot/upload-credential 接口现在返回 404。官方修复是真实的。

多位开发者在不同平台上验证了相同现象:macOS 上的 ferstar、Windows 上的 NodeLoc 用户、知乎上的「白羊武士弗拉明戈」和冯若航。多人独立验证同一个问题,这在安全事件里是强信号。

值得注意的是,ferstar 复查时发现一个细节:客户端代码里没有硬编码 OSS bucket 地址,存储目标由服务端动态下发。这意味着即使封禁了当前接口,理论上可以通过更新重新接入新的上传端点。修复是代码层面的,不是架构层面的。

被消失的更新日志和太原公司的12项追问

9 月 21 日左右,有人发现智谱官网的更新日志里,9 月 16 日的一条版本记录连同更新说明一起消失了。这条日志原本提到「代码库索引优化」,消失后对外口径变成了「已修复相关问题」。

太原一家科技公司向智谱发出公开函,列出 12 项问题,要求在 10 月 10 日前回复。追问内容包括:已上传多少用户数据、数据是否仍在云端存储、智谱是否已将数据用于模型训练、企业用户如何获取自己的数据副本等。

截至本文截稿,智谱未对这 12 项追问作出公开回应。

这里有一个结构性问题。官方道歉针对的是「功能设计失误」,太原公司的追问针对的是「数据实际影响」。两者不在同一个层面。道歉解决的是产品责任,追问解决的是数据主权。

程序员现在该做什么

先解决当下的问题。

Ferstar 的自查路线很朴素:打开终端,查 ~/.zcode 目录,看 v2/checkpoints 下有没有 .enc 文件,state.jsonlastAcceptedManifestHash 是否非空。

锁目录比关开关管用。两个 UI 开关——「优化计划」和「仓库快照索引」——都被独立复现证明无法阻断上传链路。真正有效的止血手段是在操作系统层面禁止写入。

macOS 和 Linux 下用 chflags schg ~/.zcode,Windows 下用 icacls 拒绝权限。更彻底的办法是把 .zcode 目录 mount 成一个不可写的内存盘,或者直接用容器跑 ZCode,让数据进不去也出不来。

选型底线值得重新审视。默认开启且无法关闭的数据收集功能,不管是叫「代码库索引」还是「服务优化」,在开源项目和商业项目里的容忍度完全不同。对商业项目,.git 里藏着内网地址、历史 key、未推送分支,这些泄露出去就是事故。

如果一款工具在隐私边界上如此模糊,选型时就要把「你能不能审计它做了什么」纳入评估维度。AI 编程工具不是黑盒,至少不该是。

这件事敲的行业警钟

AI编程工具的「代码库索引」正当性在哪

智谱的口径是「为了帮你快速理解仓库结构,生成 wiki 页面」。这个理由立得住的前提是:权限最小化、数据可追溯、结果可控制。但现实是:全量历史打包、私钥不在用户手里、开关关不掉、隐私政策没提。正当性从第一步就开始失守。

问题不在于「索引仓库」这个想法本身,而在于执行方式完全绕开了用户知情权。开发者逆向客户端后发现,上传链路根本不经过智谱自有业务服务器中转,而是直接上传至阿里云 OSS 对象存储。用户看到的 UI 开关管的是训练数据,管不了快照打包。

服务方掌握私钥的边界在哪里

双层加密的设计初衷可能是好的——AES 加密数据本身,RSA 加密密钥,防止中间人截取明文。但关键在于:公钥服务端下发,私钥服务端持有。

这种设计在技术上是自洽的,但在信任模型上是断裂的。用户无法验证包里装了什么,服务端拥有一切。合理的做法应该是:要么私钥也下发到本地供用户核验,要么加密包根本不上云只做本地分析,要么至少提供一个可审计的文件清单让你自己核对。

信任一旦建立,重建成本有多高

Ferstar 的推文 13 小时 27 万浏览,知乎和 V2EX 上大量开发者独立复现,太原一家公司发函 12 项追问,期限 10 月 10 日。事件的扩散速度远超修复速度。

事件发酵期间,一条 9 月 16 日的更新日志从官网和 changelog 里同时消失了。官方解释是「误操作」,但这个解释本身需要证据支撑。删除记录的行为加剧了信任危机。

对于团队来说,这件事的教训很具体:选 AI 编程工具时,隐私条款要看,数据流向要问,加密方案要审。更重要的是,默认开启的数据采集功能,哪怕名字叫「优化」,也应该被视为高风险功能,需要主动关闭而非被动接受。

这行有个潜规则:好用的工具未必是安全的,安全的工具未必好用。但当工具默认把你的底裤上传到云上,还让你关不掉的时候,好用的代价就太高了。

参考文献

[1] 上传用户仓库?智谱道歉:已修复,将开源ZCode. https://www.163.com/dy/article/L74P1LBD0511A6N9.html [2] 扒一扒 ZCode 静默上传全量 Git 历史的骚操作. https://t.co/tFT9Javr8z [3] 智譜ZCode被曝後台整庫上傳,連Git歷史也不放過. https://m.cnyes.com/news/print/6610113 [4] 智谱ZCode上传用户仓库?官方道歉:已修复,将开源. https://i.ifeng.com/c/8wWnBzJAx6Z [5] 智谱ZCode被曝后台整库上传,连Git历史也不放过. https://www.binance.com/vi/square/post/367920655820538 [6] 智谱ZCode被扒"静默打包上传完整Git历史",当天道歉、宣布 .... https://post.smzdm.com/p/a82x3g70

点这里一键关注『计算机魔术师』

如果浏览器无法直接唤起微信,可在微信内打开公众号主页:计算机魔术师

下一篇:
从Gemini误入3家企业,看AI安全测试那道看不见的边界

分享到这些地方