大家好,这里是最佳拍档,我是大飞。智谱ZCode静默上传代码的事,我估计大家应该都听说了。整场风波的起点,说起来还有点戏剧性:一位名叫ferstar的开发者在9月17号晚上还在技术群里向别人推荐智谱旗下的AI编程工具ZCode。转天清理一台只有256G存储的MacBook Air时,他发现用户目录下的zcode文件夹居然占用了超过700兆空间。这里面大约257兆是本地会话数据库和执行日志,130兆左右属于应用程序和运行依赖。
真正引起他注意的,是一个叫v2/checkpoints的目录,里面放着一份超过300兆的加密文件。对应的状态信息显示ZCode扫描了他本地打开的一个商业项目,把大约345兆的工作区内容打包、加密做成了一份313兆的基线全量快照,然后尝试上传。因为文件体积等原因这次上传连续失败了564次,所以加密包就一直停留在本地的pending目录里等着重试。
不过这个313兆的商业项目压缩包最终并没有成功传出本地,ferstar后来通过路由器连接记录和流量日志确认相关数据没有离开局域网。但问题恰恰出在这里:ZCode为什么要在用户没有主动操作的情况下,把整个项目做成只有服务端才能解密的文件还反复尝试上传呢?为了搞清楚这份加密包到底要送去哪里,ferstar进一步查看了ZCode的运行日志。
他还拆解了客户端的app.asar文件,还原出来的上传流程是这样的:ZCode先向zcode.z.ai请求快照上传凭证;服务器随后下发阿里云对象存储服务OSS的表单签名、文件路径、大小限制和RSA公钥;客户端在本地把项目压缩成tar.gz格式,用AES-256-CTR加密;再用服务器下发的RSA公钥封装密钥。最后把文件直接上传到阿里云OSS,注意这不是一段没启用的预留代码。
除了那个上传失败的商业项目之外,ferstar还发现一份只有538个文件、加密后大约15KB的公开代码仓库快照,状态已经显示被服务端接收了。而且其他Windows用户也复现出了相同的目录、状态文件和上传机制。ferstar随后查看了本地留下的快照文件清单,在一个包含四万两千多个文件的样本中.git目录占到了整个数据包的86.6%。
其中Git大文件存储缓存占56.8%,Git对象库占29.6%,引用日志操作记录占0.2%。真正的当前源代码和文档反而只占大约13.4%,这意味着ZCode准备上传的,不只是模型完成当前任务需要的那几行代码。还可能包括完整的提交历史、已经从当前版本删除的配置和密钥、还没推送的本地分支、内部GitLab域名以及所有的历史操作记录。
更关键的是,用户当时几乎没法通过产品界面阻止这件事。说到这儿可能有人会问:设置里难道没有相关的开关吗?旧版ZCode的界面里确实有体验优化和仓库快照索引两个相关选项。按照正常理解,用户把这些功能关了客户端总不该继续把整个仓库往云端传了吧?
但是ferstar对3.12.3版本的代码检查之后发现根本不是这么回事。体验优化这个开关只决定用户数据是否被授权用于模型训练;仓库快照索引这个开关只控制服务器对已经上传的快照建不建索引。两个选项都不会阻止客户端在本地生成快照,也不会关闭上传链路。
所以说相关的上传组件在用户登录之后就被加载了,根本没有通过用户偏好设置做前置判断。代码里还有两类触发条件:一类是在用户每次发送提示词之前执行快照;另一类和仓库维基更新任务有关。ferstar的一段会话日志里最多出现过62次快照事件,他曾经手动删掉了那份待上传的313兆文件。
结果大约半小时之后ZCode又重新生成了一份,失败次数也从564次增加到了565次。ferstar还翻了ZCode当时的隐私政策,上面只明确提到了平台会收集用户在对话中提交的文本、文件和代码。没有任何一处披露客户端可能打包完整工作区、打包完整的Git历史再上传云端。
ZCode说的数据优化计划默认关闭也主要是针对训练用途,不等于工作区数据不会被上传。这彻底惹怒了ferstar:9月18号他公开了完整的调查,并且在智谱GitHub的官方反馈仓库提交了一个Issue。一连抛出了三个问题:第一个问题是为什么上传范围是全部而不是当前任务上下文?
一个检查点回滚功能为什么要连用户仓库历史里早就删掉的旧密钥一起端走?这些数据对回滚有什么必要性,还是说目的本来就是拿数据?第二个问题是为什么加密设计成只有你们能看、我们自己都打不开?加密本身是标准做法,但是智谱的实现是RSA公钥由服务端动态下发、私钥只存在智谱的云端。
结果就是用户本地那份313兆的加密文件,用户自己解不开;用户不仅解不开连ZCode客户端自己都解不开。全世界只有智谱的服务器能解开,一个声称服务于用户的备份功能为什么要刻意保证用户自己无法查看自己的数据?第三个问题是为什么隐瞒、为什么关不掉。
隐私政策里没有一个字提到会整包上传用户的全部Git历史;设置里的开关管的要么是训练数据、要么是服务端建索引。快照上传本身没有任何开关,负责上传的附属进程登录即无条件启动;删掉检查点文件半小时内自动重新打包重传,一次失败重试了564次也要传上去。
最后他提了五项要求:包括公开解释这个功能的数据流向;立即提供真实生效的关闭开关、而且默认关闭;修改加密方案,让用户对自己的数据拥有解密能力;修订隐私政策如实披露快照上传的范围与频率;以及说明已上传数据怎么处理、是否提供删除渠道。他说这不是bug反馈,这是对产品诚信的质问。
随着事情不断发酵智谱终于出面了。当天下午五点四十四分,智谱在用户群发布了说明并致歉。到这个时候ZCode在特定版本中存在仓库数据上传行为已经不再只是开发者单方面的逆向推测了。智谱把问题归因于ZCode的代码库索引功能,按照官方的解释:这个功能原本是用来在本地生成仓库索引。
支持会话检查点恢复、历史版本回退和仓库维基这些能力的,其中仓库维基在云端生成知识库页面的时候可能触发仓库数据上传。因为这个功能上线初期默认是开启的,所以影响了部分用户。智谱同时表示:上传的数据会在维基页面生成后立即销毁、不会保存;相关问题已经修复。
公司接下来会开源ZCode代码库,引入第三方评估人员进行审查并且公开审查进展。换句话说这份回应其实确认了两个事实:一是ZCode确实存在仓库数据上传,而不只是在本地创建检查点;二是相关功能早期确实是默认开启的。到了9月20号的时候ZCode官网提供的最新版本是3.14.0。
ferstar对这个版本重新检查之后说相关的上传代码已经被移除了,服务器的上传接口也返回404了。但是智谱承诺的产品开源和第三方审查目前还没有看到完整落地,所以历史数据是不是被彻底删除了?过去究竟有多少用户受到了影响?这些还没有办法从外部独立验证。
更重要的是“用完即销毁”目前还只是企业的单方面声明,外部研究者虽然可以检查客户端有没有发送数据却没法进到智谱的服务器里去验证文件什么时候被删的、备份里有没有副本、加密私钥由谁来管理以及历史上传的数据是不是已经被彻底清理了。所以目前至少还有三个问题还没有公开答案:第一,影响范围到底有多大?
ZCode只说部分用户受到影响没有公布默认上传功能持续了多久、涉及哪些客户端版本、多少仓库曾经成功上传,里面有没有包含企业的私有代码。第二历史数据是不是已经彻底删除了?官方说用完就销毁但是还没有公布服务端日志、数据保留策略、删除证明或者第三方审计结果。
客户端更新只能阻止新的上传不能自动证明旧数据已经不存在了。第三开源承诺会怎么落地?到目前为止ZCode已经公开了插件市场等项目,但还没有看到包含完整桌面客户端和历史上传组件的开源代码。真正有审计价值的不仅是修复之后的最新版本还包括出问题的时候的历史代码、服务端接口设计以及数据生命周期的说明。
就在昨天这件事又升了一级,太原承明科技有限公司向ZCode客户端的运营方北京智谱华章科技有限公司发了一封正式函件。指控ZCode在未经充分告知和授权的情况下上传了该公司的数据资产和商业秘密,函件里提出了一长串要求:数据要彻底删除、流向要说明白、操作日志要提供、责任主体要明确。
甚至还问到了数据是不是可能出了境,承明科技给智谱的期限是10月10日前给出书面答复。同时明确说保留索赔、向监管部门投诉举报以及提起诉讼的权利,这说明这场风波已经不是开发者在社区里吐槽两句那么简单了。它正式进入了企业与企业之间的法律博弈层面,而且更值得玩味的是:按照承明科技的说法他们独立取证后发现相关上传根本不是偶发的代码片段传输。
而是自动、批量触发的完整归档,涉及的内容包括项目源代码、系统架构、版本控制历史、数据库口令、云服务凭证甚至员工个人信息。承明科技还提到了一个细节:智谱在9月18日公开致歉的当天凌晨他们仍然检测到了上传行为,所以对智谱所说的问题已经修复到底修在了什么时候、效果究竟怎么样他们表示怀疑。
函件还把争议引向了两个之前没有被充分讨论的问题:一个是之前上传的数据是不是已经彻底删除了;另一个是这些数据有没有可能发生了跨境传输。承明科技注意到ZCode的部分网络请求指向的是新加坡主体,但是产品服务协议签的是北京智谱华章。所以他们要求智谱说明实际的数据处理者是谁、数据存在哪里、有没有共享给第三方。
到目前为止智谱方面还没有就这封函件作出公开回应,其实ZCode并不是第一款因为代码上传机制陷入争议的AI编程工具。就在今年7月xAI旗下的Grok Build也被发现会把用户代码库上传到Google Cloud。安全机构Cereblab的调查显示Grok Build的上传范围不止是完成当前任务需要的代码。
还可能覆盖完整的代码库、被忽略的文件以及已经删除但是仍然能从版本历史中恢复的数据。当时马斯克旗下的xAI发现后关闭了自动上传功能,马斯克本人也表示会删除此前上传的数据。Cereblab当时做了一个很有意思的测试:他们用大约12G的不可压缩数据做实验,最后记录到有大约5.1G通过存储通道被接收了。
而同一会话中实际的模型请求只用到了192KB的数据,也就是说模型推理只用了不到两百KB却上传了超过5G的仓库内容。这说明这是一个独立的仓库上传系统而不是正常的模型上下文传输,这些上传的目的地是Google云端存储的一个存储bucket名字叫grok-code-session-traces。
当然这并不代表这个bucket是公开的也不代表有人访问了这些代码,但它确实证明了代码离开了开发者的机器被远程存储接收了。两起事件的表象非常接近:产品为了理解一个代码仓库最终却把远超当前任务需要的数据送上了云端。区别在于Grok Build主要被质疑没有正确尊重文件排除和隐私设置;ZCode的问题则更进了一步。
涉及完整的Git历史、不可关闭的上传链路以及解密私钥仅由服务端持有等等,这也是为什么不能简单用AI编程工具本来就需要读取代码来解释这次的ZCode事件。AI编程工具确实不可能完全不接触代码,当你让智能体修改一个登录模块的时候模型不仅要看到当前文件。
还需要找到它调用的接口、相关的数据结构、测试文件以及项目里的各种约定,问题在于一个大型代码仓库可能包含几万甚至几十万个文件不可能每次都全部放进模型的上下文窗口里。所以业内通常有三种技术处理方式:第一种是按需读取,智能体先在本地查看目录结构用关键词、文件名、符号和正则表达式搜索。
找到可能相关的文件再把其中一部分内容发给模型,这种方式上传的数据比较少但是模型对整个项目的理解依赖搜索过程可能会漏掉一些间接依赖关系。第二种是代码库索引,客户端把源代码切分成函数、类或者若干代码块再为它们生成向量、符号索引和文件关系。当用户提出问题的时候系统先从索引中检索相关代码只把命中的片段交给模型,这就相当于先给代码仓库做了一张可以搜索的地图。
第三种是云端工作区系统直接把代码仓库复制到远程虚拟机或者容器里由云端智能体完成检索、修改、测试和构建。这种方式适合长时间自主运行的智能体但代码进入云端是产品工作的前提所以必须明确告知用户并且提供仓库授权、数据保留和删除机制。我们拿Cursor来举例,Cursor采用的是前两种方式和云端智能体并存的模式。
Cursor在今年公布的技术说明里明确提到:当一个代码库首次建立索引的时候,索引管线会上传每个文件把代码切分为语法块并且生成用于语义搜索的嵌入向量。后续再利用Merkle Tree算法来比较文件哈希只同步发生变化的部分避免每次重新上传整个仓库。也就是说Cursor同样需要读取甚至上传代码,区别不在于代码有没有和服务器发生交互。
而在于数据范围和用途能不能解释得通:Cursor建立语义索引的时候处理对象主要是代码文件及其语法片段;当文件发生变化时只同步对应分支和代码块。它的智能体检查点则用于撤销智能体所做的修改,官方文档明确说检查点保存在本地、独立于Git只捕获被修改文件的状态。
而不是把完整的Git对象库作为回滚数据上传到云端,ZCode的问题恰恰出在这里。智谱说相关上传是为了支持会话检查点恢复、历史版本回退和仓库维基,但是开发者发现问题版本生成的快照里86.6%的内容来自.git目录。包括Git对象库、大文件存储缓存和引用日志,真正的源代码和文档只占大约13.4%。
如果目的只是恢复智能体修改过的文件通常只需要记录修改前后的文件差异或者在本地保存检查点就够了,没有明显必要把完整的Git历史也一起上传。如果目的是生成仓库维基系统确实需要分析代码结构,但同样很难解释为什么需要大文件存储缓存、历史提交中已经删除的密钥以及未推送分支的操作记录。
换句话说ZCode不是因为采用了云端索引就天然有问题,而是它实际收集的范围和官方解释的功能目的之间存在明显的落差。当然了Cursor也不是绝对安全的,它的官方技术文章明确承认新代码库建立索引时会上传文件。而且为了让同一团队复用已有索引服务器还会保存代码块索引、文件哈希和加密后的路径。
它提供的隐私模式主要承诺数据不被用于模型训练并不意味着代码不会为了完成推理和索引而经过服务器。所以是否用于训练、是否上传服务器和是否长期保存其实是三件不同的事:关闭训练授权不等于代码不会被发送;数据经过加密也不等于服务提供方无法读取。
文件被声明为临时使用也不等于用户能够验证它已经被销毁了,而这些容易被混为一谈的数据承诺实际上指向了一个比单次上传事故更大的问题:当我们讨论一款AI编程工具安不安全的时候,我们到底是在信任模型还是在信任模型背后的那整套软件系统?国外播客主科尔·摩根(Cole Morgan)在对Grok Build事件的复盘视频中提出外界往往把注意力集中在了模型本身。
比如模型会不会遵循指令、会不会受到提示词注入的影响、有没有可能泄露敏感信息,但是像Claude Code、Codex、Cursor和Grok Build这些产品它们并不只是模型还包括包裹在模型外部的一整套Harness框架。所以真正需要审查的对象不能只有模型还应该包括负责赋予模型权限、调度工具和处理数据的这个Harness本身。
这里面其实存在两个不同层次的信任问题:第一个层次是模型会不会滥用用户有意赋予它的权限?比如用户允许智能体读取项目目录之后,模型会不会去访问和任务无关的敏感文件。第二个层次是Harness会不会在模型任务之外执行额外的操作?比如用户只授权智能体修改一个文件外围软件却在后台建立了整个仓库的快照并上传。
除此之外还有第三层风险:连接到Harness上的MCP服务器、各种插件、遥测服务、日志系统、模型供应商和云存储厂商。每增加一个外部组件都意味着数据可能进入新的权限体系和新的供应链,这意味着即使模型本身没有发生越权包裹模型的软件和它所依赖的各种服务仍然可能制造出巨大的安全暴露面。
回到ZCode这件事上我们会发现它其实同时触碰了后面两层红线:加密私钥只由服务端持有这是Harness层面的架构设计选择和模型能力没有关系;上传组件在用户登录后无条件启动、不受任何设置开关控制这也是Harness层面的实现决策。
甚至连上传的范围远远超出功能解释的合理边界,本质上也是Harness的数据处理策略问题。模型本身只是一个根据输入生成输出的引擎,它不会决定要不要打包整个仓库、用谁的公钥加密、失败了重试多少次。这些都是写在Harness代码里的确定性逻辑,所以这件事给整个行业的提醒其实很明确:在AI编程工具越来越普及的今天,用户选择一款产品的时候,不能只看模型聪不聪明、补全准不准确,还得看包裹模型的那层软件系统值不值得信任。
代码是很多公司最核心的资产,一旦泄露,造成的损失可能是不可逆的。对于正在使用或者打算使用AI编程工具的开发者和企业来说,大飞这里有几个建议:第一,仔细阅读产品的隐私政策和数据处理说明,特别注意数据会不会离开本地、用于什么用途、保存多长时间。第二,不要想当然地认为设置里的开关都会起作用,关掉之后就能完全阻止数据上传。如果对安全要求比较高,可以考虑用网络代理或者防火墙工具实际监控一下流量。