装一个 AI 编程助手,登录一次账号,它就能顺手把你本地的整个仓库——包括完整的 .git 目录、Git LFS 缓存、reflog 和全局配置——打包加密,上传到云端。开发者 ferstar 对 Z.ai 的桌面端工具 ZCode 做了一轮逆向,把这件事从猜测变成了可复现的证据链:一次实测快照带走 42,411 个文件、313MB 数据,其中 .git 目录占载荷的 86.6%。
一次登录,四万多个文件被打包走了
打包清单比你想的野
42,411 个文件、313MB,这组数字里真正说明问题的是文件数。几万个文件意味着打包逻辑根本不是"挑几个相关源码当上下文",而是把工作区目录整个递归扫了一遍。正在跑的服务日志、构建残留、编辑器缓存、被 .gitignore 挡住的本地配置,全都没有豁免权。凡是落在那个目录树里的东西,都进了清单。
.git 吃掉八成六,这不是顺手
313MB 里 86.6% 来自 .git。这是整件事里最扎眼的一笔。工作区里的代码你也许不介意被读,但 .git 里躺着的是另一类东西:完整提交历史、每个分支的演化痕迹、reflog 里那些被 reset 掉、被 rebase 抹平、被 force push 覆盖的提交,以及 Git LFS 的本地缓存。很多团队的安全事故不出在当下的代码上,而出在"半年前删掉的那个 config 文件"上——密钥硬编码进去,后来改走环境变量,历史里还留着。reflog 更狠,它记录的是你原本没打算让任何人看见的操作轨迹。
加密了,然后呢
载荷在离开本机之前被加密,这一点得承认,它至少降低了传输链路和存储桶被直接翻看的机会。但加密改变不了一个事实:数据已经不在你的机器上了。密钥由谁生成、存在哪、算法是否经过审计、服务端有没有拿到明文副本——这些你都无法从外部验证。对象落在 Aliyun OSS 上,意味着密文处于一个可被云厂商、可被调证流程触达的位置。对多数团队来说,真正的风险不是"有人当场解密了我的代码",而是"我从来不知道这些数据出去过"。
开关按了,上传没停
一个 UI 开关能管住几件事
这类工具里通常都摆着一个看着很让人安心的设置项——遥测、改进计划、数据共享之类的名字。用户点掉它,心里默认"这下不上传了"。ferstar 的取证指向另一种可能:那个开关管的是另一条通路。崩溃日志和匿名统计走一条路,工作区索引和上下文打包走另一条,两者共用一个设置页面,底层却是两套逻辑。开关关掉了自己那一部分,剩下的照跑不误。
默认值就是产品态度
更值得琢磨的是默认状态。一个功能默认开启、藏在设置深处、关了还可能失效,它的产品优先级就很清楚了——上传这件事,在厂商的排序里比用户的知情权靠前。这未必出于恶意,很多团队会把它包装成"为了给你更好的补全和检索体验"。但意图不改变结果:用户在不知情的状态下交出了远超预期的数据量。
怎么把这件事钉成事实
从进程的读取行为入手
逆向这类客户端,光看网络流量不够——载荷是加密的,抓包只能看到体积和时间点。ferstar 的路子更聪明:盯住进程实际打开了哪些文件、读了多少字节。把系统调用层面的文件访问记录和最终上传的载荷大小做对照,.git 里的对象文件、reflog、LFS 缓存被逐一读出,证据链就闭合了。再配一次受控实验——往工作区塞一个体积特征明显的文件,看下次快照是否跟着变大——因果关系就没法辩驳。
设置项的真假也能测
把开关打开、关掉,各跑一次,对比两次上传的文件清单和体积。如果差异为零,那这个开关在数据外发这条路径上就是装饰品。这种测试不需要多深的逆向功底,任何一个愿意花半小时的开发者都做得出来。遗憾的是,绝大多数人从来没试过。
把边界画在文件系统上
别让 agent 直接看到你的仓库根目录
最有效的办法是把 AI 编程工具塞进容器或者虚拟机,只把需要它编辑的子树挂进去,用只读绑定挂载或者独立的工作副本,让 .git 对象库留在宿主机上。git 原生的 worktree 机制在这里格外好用:工作目录里只留一个指向外部仓库的 .git 文件,真正的对象库和 reflog 根本不在那棵树里,打包逻辑再怎么递归也扫不着。
权限是最后一道闸
文件系统权限在这种场景下被严重低估。把 .git 目录的读权限从运行 agent 的那个用户手里收走,多进程、多用户环境下立刻见效。macOS 上可以用沙盒配置限制进程能碰的路径,Linux 上 mount namespace 加 seccomp 是标准操作。原理很朴素:进程只能读它有权读的东西,把最敏感的那部分权限摘掉,再勤奋的采集逻辑也拿不到数据。
出网也别全放开
把客户端进程的出网目标收窄到它真正需要的域名,对对象存储端点做审计甚至直接阻断,等于在数据离开机器的最后一跳上加了一道关卡。这不是万能药——工具可能换域名、绕 CDN,但这道关卡至少能让"静默"两个字失效,把外发行为从默认发生变成需要你点头。
真正的问题不在 ZCode
你的 .git 是一份你没读过的档案
任何拿到你 .git 目录的人,拿到的不是代码,是一份关于你和团队的档案:谁在什么时候提交了什么,哪次事故被回滚过,哪个分支被放弃了,密钥在哪个提交里出现过又消失。这份档案的价值远高于当前这一版源码。多数开发者花在保护生产密钥上的精力,远超花在历史记录上的——而泄露的往往是后者。
Agent 运行时的信任模型还是空白
这件事的意义在于它暴露了一个行业级空洞:AI 编程工具到底该拿到多少访问权限,目前既没有共识,也没有标准。IDE 插件、命令行工具、桌面客户端的权限模型各不相同,用户能做的判断极其有限。规范落地之前,唯一可靠的做法是把信任边界画在自己这边——用隔离、权限和网络策略,替厂商把该有的克制补上。ferstar 这次逆向的价值不在于揪住某一个产品,而在于演示了一遍:怎么验证你机器上那个 agent 到底在干什么。

