
智谱 ZCode 的“静默上传 Git 历史”风波最近几天在开发者社区里炸得厉害。我身边好几个团队第一时间把 ZCode 装上试用又在本周内紧急卸掉还有人连夜翻自己电脑上的访问日志生怕私有代码已经被悄悄送去做了模型训练。作为一个长期和各种 AI 编程工具打交道、同时还负责团队 Git 仓库管理的开发者我今天不打算站队骂街只想把这次 48 小时信任危机从头到尾拆开看ZCode 到底做了什么触碰底线的事Git 历史为什么比源码本身更敏感我们这些普通开发者怎么自查、怎么补救以及以后再装任何 AI 编程工具时应该守住哪些红线。这篇文章适合所有用 Git 管理代码、又对 AI 辅助编程有好奇心的朋友内容不涉及内部爆料全部基于公开信息和常见技术手段的合理推演你可以直接对照自己的环境做一遍检查。1. 事件还原48 小时里到底发生了什么1.1 争议起点从“AI 编程助手”到“偷代码风波”ZCode 是智谱推出的一款 AI 编程工具主打让开发者通过自然语言生成代码、修改项目文件还能把智谱的 GLM 模型能力直接接进 IDE 工作流。这类产品近两年非常多Cursor、Trae、WorkBuddy 都在抢同一个赛道ZCode 的优势在于背靠智谱的模型底座又有免费 token 活动吸引用户所以最初口碑还不错。但争议很快就来了。社区里陆续有人发现ZCode 在运行时会读取用户本地的 Git 历史记录并且存在将用户代码打包上传到云端的行为有开发者通过抓包发现自己项目里的文件内容被发送到了与阿里 OSS 相关的存储地址。这个发现一传开舆论瞬间从“国产 AI 编程工具崛起”变成了“ZCode 偷代码”。说句公道话AI 编程助手把必要上下文发送到云端本身不算十恶不赦Cursor 也会把相关代码片段发给 OpenAI 处理很多工具默认开着遥测上报。真正让开发者愤怒的是“静默”这两个字。1.2 社区排查的关键节点抓包、日志与连接追踪让我把时间线大致捋一下。第一天有用户在 V2EX 和掘金发帖说自己打开 ZCode 后发现电脑的网络连接里出现了大量陌生的 HTTPS 请求用 Wireshark 抓包后发现请求指向的域名和本地项目路径存在规律性关联怀疑工具在后台遍历文件系统。第二天更多开发者开始跟进测试方法五花八门有人用 Charles 做 HTTPS 中间人代理有人用系统自带的进程网络监控有人直接看 ZCode 的日志目录结果矛头都指向同一个结论——ZCode 不仅仅会上传当前打开的文件还会主动收集整个工作区目录下的.git信息。最离谱的一条线索是有用户发现 ZCode 的安装目录里生成了打包缓存里面包含了他上一个公司的 Git 提交记录和作者邮箱而这个项目他最近根本没在 ZCode 里打开过。第三天官方出面说明具体措辞我不评价但核心意思大概是“相关功能是用于代码分析与建议数据传输经过加密”。问题是任何一个被读取过 Git 历史的人都明白这已经不是加密问题而是授权问题。Git 历史里的提交邮箱、内部主机名、分支策略、甚至被删除但还留在 reflog 里的旧密钥这些东西一旦被收集加密传输只是让“偷”的过程更隐蔽并没有改变“未经同意就拿走”的事实。1.3 舆论焦点真正的问题不是功能而是开关和知情权我不认为 ZCode 团队是恶意写了一个“偷代码模块”更合理的推测是产品为了提升代码补全和问答质量把上下文收集做得太激进默认开启了整个仓库级别的索引和分析但 UI 界面上没有清晰告知用户也没有给出可选择的关闭开关。很多用户是在不知情的情况下被收集了远超预期的数据。这就引出了软件工程里一个非常经典的问题一个功能是否正当不取决于它是否“有用”而取决于它是否“被知情、被授权”。远程协助工具能读取你的屏幕但你必须主动发起会话这不叫偷。杀毒软件会扫描全盘但安装协议里写明了并且你可以关闭这不叫偷。而一个编程工具悄悄把你的.git目录纳入采集范围既不弹窗提示也不在设置里放一个显眼的开关那无论它的目的是训练模型还是优化推荐在用户视角里这就是偷。提示如果你现在正打算试用 ZCode我的建议是先搭建一个隔离测试环境不要直接把公司仓库导进去。后面第 3 节我会写具体怎么自查和隔离。2. 信任危机的技术根源为什么 Git 历史是最高敏数据2.1 .git 目录里到底藏着什么很多不熟悉 Git 内部机制的朋友可能不理解为什么大家那么怕工具读取.git目录难道它跟直接读源码有什么本质区别吗区别非常大。源码文件是静态的你看到的只是当前快照。而.git目录是活的它记录了整个仓库生命周期的全部信息。这里面至少有这么几层提交对象objects所有历史版本的源码快照包括已经删除的文件、已经废弃的代码、曾经的错误尝试。你以为把文件从工作区删掉就没事了只要它被提交过就永远躺在 Git 对象库里直到你做了历史重写和 gc 清理。引用与日志refs、logs分支指针、HEAD 移动记录、reflog 里保存的每一次 checkout、reset、commit --amend、rebase 操作。通过 reflog 可以还原出开发者几周前做了什么操作。配置config这个仓库的 remote 地址、用户名、邮箱、HTTP 代理配置等。remote 地址经常包含公司内网的 GitLab 或自建服务器的域名一条就能暴露内部网络拓扑。钩子与订阅hooks、gitmodules团队自定义的 CI 脚本、钩子函数、子模块地址全部都是情报。换句话说一个工具拿到了你的.git目录相当于拿到了你所有代码版本的完整备份、你整个团队的协作习惯、你的邮箱和身份信息、甚至你公司内部的服务器地址。这比单纯读几个源码文件严重一个数量级。2.2 “静默”比“上传”更致命信任模型的崩塌我觉得这次危机里最值得行业反思的不是“上传”本身而是“静默”。开发者使用 AI 编程工具的正常信任模型是什么是我打开这个文件工具把这个文件的相关上下文发给模型换取代码补全或修改建议这是一个有来有回的等价交换。但 ZCode 事件里用户发现工具在后台读取的是整个 Git 历史这就打破了信任模型用户根本不在这段对话里却成为了数据来源工具没有向用户说明会读取哪些范围用户就无从判断自己的哪条提交记录、哪个内部邮箱、哪段废弃代码已经被送走。更麻烦的是一旦工具把读取范围扩展到.git目录普通用户的“删除即消失”直觉就失效了——你删掉的文件其实还在 Git 对象库里你没有删除权限但工具可以读取。“静默”还带来了第二个致命问题它剥夺了用户采取预防措施的机会。如果工具首次启动时弹窗说“我会读取你的 Git 历史用于代码分析是否允许”用户至少可以做一个知情决定。但静默执行意味着等你发现的时候数据往往已经上传到对方服务器了本地拦截根本来不及。2.3 正常遥测与越权读取的分界线在哪里这里我还想多说几句边界问题因为很多人容易走极端一看到工具上报数据就喊“全是在偷代码”。实际上现代软件产品默认带遥测是非常普遍的关键要看三条分界线第一条是范围分界线收集的数据是否超出了完成核心功能所必需的最小范围比如 IDE 插件上报崩溃日志、统计快捷键使用频率没问题读取当前打开文件的摘要用于补全可以接受但遍历整个.git对象库和 reflog就很难用“必需”来解释了。第二条是告知分界线数据收集行为是否在用户可感知的入口如设置页、首次引导、隐私政策弹窗中明确列出用户是否需要主动“深挖”才能找到还是被默认藏在繁杂的协议条款里第三条是控制分界线用户是否可以无副作用地关闭数据收集如果没有关闭按钮或者关闭后功能就会罢工那这个“开关”就是伪命题。如果拿这三条去对照 ZCode 这次的行为我只能说它至少在告知分界线这一条上做得明显不到位。这不是“有没有加密”能搪塞过去的问题。3. 自查与取证如何判断你的代码有没有被上传过3.1 四步定位法从网络连接到文件访问痕迹很多朋友看到新闻后第一反应是“完了我用过 ZCode怎么查它到底传了什么”别慌下面是可以在 Windows、macOS 和 Linux 上通用的四步自查方案虽然不能做到 100% 追溯但能帮你判断工具是否在网络层面有可疑行为。第一步看实时网络连接。Windows 下用管理员权限打开 PowerShell执行netstat -ano | findstr ESTABLISHED把输出的 PID 和任务管理器里的进程对照重点找 ZCode 相关进程正在连接的外部地址和端口。macOS 下用lsof -i -P | grep -i zcode更直观。如果发现它一直跟陌生域名保持长连接而且这些域名不是你常用的模型 API 端点那么就需要进入下一步。第二步抓应用层流量。推荐用 Charles 或 Fiddler 配置 HTTPS 中间人代理前提是你安装了它的 CA 证书这样就能解密 TLS 流量看到 ZCode 实际请求的 URL 路径和请求体大小。如果发现请求体里有.git/路径前缀或者objects/pack这类特征字符串那基本可以实锤了。抓包要抓多久建议先完全退出 ZCode再重启从启动到打开一个大型项目整个过程持续 20 到 30 分钟记录所有流量最后按域名分组统计。第三步查文件访问痕迹。Windows 上可以用 Process Monitor 过滤 ZCode 进程对.git目录的读操作macOS 上可以用fs_usage命令实时跟踪。如果你不想装额外工具还有一个取巧的办法把某个仓库.git/config里的邮箱临时改成只有你知道的乱码字符串然后打开这个仓库等一段时间再去云端模型平台的后台日志里搜不搜得到。这个办法需要你有对应的 API 后台日志权限条件比较苛刻但效果最直接。第四步检查本地上报缓存。很多这类工具会把待上传的数据先缓存在本地再批量发送去这几个常见位置翻一翻~/Library/Application Support/macOS、%APPDATA%\Windows以及 ZCode 安装目录下的logs、cache、upload文件夹。重点筛体积突然变大的缓存文件解压后看里面有没有.git路径或与你项目同名的文件夹。注意如果你用的是公司配发的电脑强烈建议先跟 IT 或法务沟通再做抓包和日志检查避免因操作抓包工具触发安全合规问题。个人电脑上就没这个顾虑放心做。3.2 快速给 AI 编程工具“断电”的正确姿势查出可疑行为后很多人第一反应是“删掉”但你得按顺序操作否则可能把证据毁掉。正确顺序是先断开网络或退出登录让工具无法继续上报。用系统进程管理工具强制退出 ZCode但不要急着卸载。复制出疑似缓存和日志目录打包另存为“证据”。再执行正常卸载。清理相关配置残留主要是~/.zcode或 AppData 下的配置目录。我特别强调一点一定要先断开网络再退出。因为有些工具会在退出时做一次“最后同步”你直接关掉窗口它反而可能趁进程退出前把缓存一次性清空或上传。用任务管理器强制结束可以跳过这个收尾动作最大限度保留现场。3.3 从 git reflog 里找回被删除的敏感痕迹如果我们换一个思路不查工具偷了什么而是查你那台电脑上到底什么敏感信息可以被偷。最容易被忽略的就是 git reflog。Git 的 reflog 默认会记录 90 天内你在这个仓库里的所有 HEAD 变动包括你 reset 回退掉的那次提交、你 commit --amend 覆盖掉的上一个版本、你临时 stash 的改动。举个例子你上周不小心把config.yml里的 API 密钥提交了后来发现并用git reset --hard回滚了以为密钥没传出去。但 reflog 里那条旧提交记录依然存在任何能读取.git目录的工具都能把你“已经删除”的密钥从对象库里还原出来。所以自查时必须跑一次git reflog --all把所有历史操作列出来看看有没有包含敏感信息的提交。还可以用git fsck --lost-found把悬空对象找回来这些往往是被删除但还没被清理的分支和提交。4. 从 Git 出发提交历史管理与敏感信息清理实操4.1 热词里的高频问题git commit --amend、rebase 与历史重写到底怎么用这次事件带来的一个连锁反应是很多平时只会在 Git 里 add、commit、push 的开发者开始关心“历史清理”了。相关讨论里被提到最多的几个命令我在这里集中讲清楚。先说是git commit --amend。这个命令的作用是修改最近一次提交信息或者把当前工作区的改动并入上一次提交。它只能作用于 HEAD也就是最顶上的那一次提交。用法很简单git commit --amend -m 新的提交信息。如果你发现上一次提交里不小心带进一个敏感文件可以先把那个文件从暂存区移除再执行git add -A和git commit --amend。但它有一个非常关键的坑commit --amend 本质上会生成一个全新的提交对象旧的那个对象会被替换掉。如果你已经把这个提交 push 到了远程仓库直接 amend 会导致本地和远程历史分叉别人 pull 的时候会冲突。正确做法是先git push --force-with-lease强制覆盖但要确保这个分支只有你一个人在用否则会把别人的提交也搞坏。力荐使用--force-with-lease因为它会在推送前检查远程分支是否已经被别人更新安全性比裸--force高很多。再来说git rebase -i。如果你需要修改的不是最近一次而是历史中某几个提交交互式 rebase 是标准方案。执行git rebase -i HEAD~5会打开一个编辑器列出最近 5 条提交你可以把某行的pick改成editrebase 在这个过程中会停下来让你操作你可以用git commit --amend修改内容然后git rebase --continue继续。这套操作最大的风险是如果你的仓库已经被上传到了云端那么本地怎么改历史都没用该泄露的早就泄露了。历史重写只是“亡羊补牢”不是“时光倒流”。4.2 真正需要“洗历史”时的标准动作filter-repo 与 BFG当敏感信息已经深埋在几十条提交里手动 rebase 就力不从心了。这种情况业内常用的是git filter-repo。git filter-repo是一个高效的历史重写工具能按路径、按作者、按内容做全局过滤。最基本的用法是git filter-repo --path config.yml --invert-paths意思是把config.yml从所有历史提交中彻底移除。还有一种是替换邮箱git filter-repo --mailmap mailmap.txt。无论哪种执行完后你的仓库历史会被完全重写所有 commit hash 都会变因此必须通知团队所有成员强制拉取新历史旧克隆全部作废。BFG Repo-Cleaner 是另一个选择它比 filter-repo 操作更简单bfg --delete-files config.yml一条命令就能清理所有历史中的同名文件。但 BFG 在处理复杂过滤时的灵活性不如 filter-repo而且 BFG 项目本身更新频率偏低。如果你只是删一个特定文件用 BFG 确实省事如果你需要批量清理邮箱、密码、内网域名建议用 filter-repo。还有一个冷门但非常实用的工具叫git forget-blob它能找到对象库里的大文件并批量清理特别适合那种“谁把 200MB 的数据库备份提交进去了”的情况。注意无论用上面哪种工具重写历史都只是把“历史中的文件内容”删掉。如果敏感信息的泄露路径是“工具读取了文件并上传到云端”那云端已经存了就不受你控制。所以历史重写的真正价值是降低后续再次泄露的风险而不是抹掉既成事实。4.3 团队协作中的隐私红线与 .gitignore 规范这次事件也给我们团队提了个醒私有的 Git 仓库里哪些东西绝对不允许出现必须有硬性规范。我梳理了几个最容易泄露的信息类型环境变量和密钥.env、config/credentials.yml、*.pem、*.key这类文件必须写进.gitignore的全局模板并且提交 hook 里加一层扫描。内部域名和 IP线上数据库地址、Redis 地址、内网 GitLab 地址这些如果出现在代码里等于把公司内网拓扑送给别人。个人隐私成员的真实姓名、手机号、身份证号测试数据里意外混入的都算必须定期扫描。历史残留已经停止使用的旧令牌、旧 SSH key哪怕已经删了也可能在 reflog 和对象库里存在 90 天以上。团队可以在.git/hooks/pre-commit里挂一个正则扫描脚本搜password\s*、api_key、BEGIN RSA PRIVATE KEY这类特征命中就直接拦截提交。这个脚本本身不复杂但对守住底线很有效。5. 常见问题与排查技巧实录5.1 高频报错fatal: not a git repository 与 Git 环境配置问题在这次事件相关的讨论里我发现很多人都卡在最基础的 Git 环境上——他们想跑 reflog 和 filter-repo结果连git status都报fatal: not a git repository (or any of the parent directories): .git。这个报错的本质很简单你当前所在的目录不是 Git 仓库或者 Git 找不到向上游的.git目录。最常见原因有三个一是你真的在一个普通文件夹里运行命令二是你在仓库的子模块或嵌套目录里但外层.git是一个文件而不是目录submodule 场景下 Git 会用.git文件指向 gitdir三是你的GIT_DIR环境变量被设置成了错误的路径。排查思路也很直接先跑pwd确认当前路径再跑ls -la | grep .git看看仓库标记是目录还是文件。如果.git存在但命令仍然报错执行git rev-parse --git-dir它会告诉你 Git 认为的仓库目录在哪。Windows 用户如果刚装完 Git Bash 就遇到这个大概率是你没cd进项目目录直接在默认家目录下操作了。顺带说一句 Git 安装配置的问题。热搜里很多朋友问“git 安装及配置教程”这里给一条极简路线Windows 上直接下载官方 Git for Windows 安装包选项里选中“Git Bash Here”和“在终端中使用 Windows 控制台”macOS 上装好 Xcode Command Line Tools 之后自带 gitLinux 用发行版包管理器安装即可。装完第一件事就是配置用户名和邮箱git config --global user.name 你的名字和git config --global user.email 你的邮箱这两个配置会写进每次提交的作者信息里请务必用专门的工作邮箱别把私人 Gmail 混进去。5.2 我的工具检查清单装任何 AI 编程工具之前的八个问题经过这次折腾我现在给团队定了规矩任何新 AI 编程工具想在本机或公司开发机上落地必须先过八个问题的审核工具的隐私政策里是否明确列出“收集哪些数据”“用于什么目的”“保留多久”含糊其辞就是不合格。是否提供一键关闭遥测和数据收集的开关这个开关是否藏在二级菜单深处工具的本地进程是否有监听端口用netstat -ano扫一遍就能看出来。首次启动是否弹窗询问授权授权内容是“当前文件”还是“整个仓库”它读取代码时是把完整文件内容发给云端还是在本地做脱敏处理后只发摘要云端处理完数据后是否提供用户可主动清除数据的接口工具的开发方有没有被社区抓包审计过口碑如何有没有“前科”在你的网络环境下它默认连接的域名列表是否与产品文档一致这八个问题里我认为第 2 条和第 5 条是最重要的红线。一个工具可以免费送你 token也可以功能弱一点但如果它不给你关闭数据收集的选项或者处理敏感代码时不做本地脱敏那它就不配进入你的开发环境。5.3 我的个人建议隔离、脱敏、常审计三件套最后分享一个我最近在用的工作流叫作“三件套策略”恰好可以解决“想用 AI 编程工具又怕被偷代码”的焦虑。第一件套是隔离环境。我建议在电脑上划分出“私域仓库”和“公域仓库”。私域仓库里都是公司或个人的核心代码只用本地模型或完全不用 AI 工具公域仓库里放一些示例项目、开源代码或者已经脱敏的练手项目只有这些目录才对 AI 编程工具开放。这不是对工具的不信任而是一种工程上的纵深防御就像你不能因为家里装了防盗门就不锁卧室的门。第二件套是代码脱敏。在把项目交给任何 AI 工具之前先跑一次自动扫描把密钥、内部域名、个人邮箱替换成占位符。可以写一个简单的 Python 脚本读 Git tracked 文件列表然后用正则批量替换再让工具读取脱敏副本。这个过程不会花多少时间但能防止大多数“意外泄露”。第三件套是常态化审计。我不是让你每天抓包但至少每个月花十分钟做一次简单的网络连接检查特别是当你更新了工具版本之后。很多工具都是某个小版本偷偷增加了数据收集功能过一段时间再去看设置页发现多了一个灰色的小开关。这种时候如果没有日常审计你根本不知道自己的代码在什么时候、以什么方式被送出去的。我自己在这次事件里学到的最大的教训是不要把“默认设置”当作“安全设置”。一个好的开发工具应该默认保护隐私而不是默认收集一切。ZCode 这次翻车本质上不是技术问题而是产品价值观的问题。我希望每个 AI 编程工具团队都能明白Git 历史是开发者最珍视的工作日志没有用户的明确授权任何形式的读取都是越界。以后我会持续关注智谱这边的改进如果后续版本真的把权限边界做清楚了我不排除重新试用的可能——但在那之前公域和私域之间那条隔离线我会一直守住。