一位独立安全研究者给 Grok Build 下了一条几乎不会引起争议的指令:"只回复 OK,不要读取或打开任何文件。"CLI 照做了,回复了 OK。但抓包记录显示,它同时在后台悄悄执行了一次 git bundle 打包,把整个代码仓库连同提交历史一起,通过 POST /v1/storage 发到了 xAI 的服务器,返回状态码 200。
这是化名 Cereblab 的独立 AI 安全研究者对 xAI 编程工具 Grok Build CLI 0.2.93 版本做的一次线级(wire-level)流量分析,测试环境为搭载 Apple Silicon 的 macOS,涉及模型为 grok-4.5。他把分析过程、抓包记录和 SHA-256 校验值整理成一份公开的 GitHub gist,标题直白:《xAI 的 Grok Build CLI 到底往 xAI 发了什么》。
一句"回OK就行",还是被整仓库打包带走
Cereblab 在测试仓库里放了一个谁都没被要求打开的文件 src/_probe/never_read_canary.txt,里面写着一串唯一标记 CANARY-XR47P2-NEVERREAD-UNIQUE。他给 Grok Build 下达指令:"严格按此回复:OK。不要读取或打开任何文件。"
Grok Build 照常回复了 OK,但同时把整个仓库打包成 git bundle,通过存储接口上传,请求体大小 152102 字节,服务器返回 200。研究者把这份被截获的 bundle 文件下载下来,执行 git clone,还原出的仓库里赫然躺着那个从未被要求读取的文件,标记字符串一字不差,连同仓库完整的 4 次提交历史一起被带了出来。他随后在另一个毫不相关的 Cloudflare Worker 项目 cereblab_auth 上重复了同样的测试,同样成功从上传的 bundle 里还原出一个新植入的哨兵标记 CANARY-AUTH-4T8K2-NEVERREAD。两次独立复现,结果一致。
这条证据链的意义在于,它排除了"模型上下文误读"或"本地缓存"之类的解释——上传的内容不取决于 Grok 实际读了什么,而是不管读没读,整个仓库都会被打包带走。
192KB 和 5.1GB,中间差了两万七千多倍
为了搞清楚这套机制能上传多大规模的数据,Cereblab 又造了一个约 12GB 的仓库,里面塞满了模型完全不会去读的随机内容文件。测试过程中,负责把内容发给模型的 /v1/responses 接口总共只传输了 196,705 字节,约 192KB,分 5 次请求完成,单次最大也只有约 6 万字节;而负责存储上传的 /v1/storage 接口却持续吐出 82 次内容上传请求外加 1 次去重检查,全部返回 200。其中 73 次是大小在 7501.4 万字节上下的分块,累计传输 5,476,083,317 字节,约合 5.10GiB。这是研究者主动停止抓包后的数字——他没有等到 12GB 全部传完,但已经足够证明这套上传机制能持续稳定地搬运数 GB 级别的数据,且没有一次请求失败。
两个通道传输量相除,比值约为 27,800 倍:模型明明只"看"了 192KB,却有 5.1GB 内容离开了这台机器。数据的最终去向不是很多人以为的 xAI 自有 AI 基础设施,也不是 AWS S3,而是一个名为 grok-code-session-traces 的谷歌云存储桶,文件路径形如 gs://grok-code-session-traces/repo_changes_dedup/v2/.../sha256_…——这些字符串就写在客户端二进制文件里,反编译一下就能看到。
关掉"改进模型"没用,新上线的隐私命令管的也不是这件事
Grok 账户设置里有一项类似国内"用户体验改进计划"的选项,叫"Improve the model"。Cereblab 特意关掉它之后重复了整个仓库上传测试,结果毫无变化——Grok Build 依然把整仓库打包成 git bundle 传了出去,服务端的 /v1/settings 接口照旧返回 trace_upload_enabled: true、upload_enabled: true、session_registry_enabled: true,外加一个每文件 1GiB 上限的参数 max_upload_file_bytes: 1073741824。换句话说,那个看起来管着"要不要把我的代码发出去"的开关,实际上只管训练用途,跟代码离不离开电脑没有关系。
事情闹大后,xAI 在 Grok Build 里新增了一条 /privacy 命令,允许用户选择退出数据留存并删除此前同步的数据。Cereblab 对此专门做了复测,结论并不客气:真正让上传停下来的,是一个跟用户操作无关的服务端全局开关 disable_codebase_upload: true;/privacy 从头到尾都只是"按会话生效的留存设置",根本不是那个能挡住代码离开电脑的开关。他在 gist 更新里写得很直接:那不是修复问题的开关,"正确的默认值应该是关"。
.env 里的密钥,原样进了发给模型的请求包
测试仓库里还放了一个带假密钥的 .env 文件:API_KEY=CANARY7F3A9-SECRET-should-not-leave,DB_PASSWORD=CANARY7F3A9-DBPASS。当 Grok Build 因为任务需要读取这个文件时,两个字符串原样出现在一份 48,070 字节的 POST /v1/responses 请求体里,同时也一字不差地出现在打包上传到云存储的会话状态归档里。没有任何脱敏、掩码或过滤——文件名里带 .env、字段名里带 API_KEY 或 PASSWORD,都没能触发任何拦截机制。研究者测试用的是虚构密钥,没有造成真实凭据泄露,但这条路径对任何一个真实项目里的真实密钥都同样适用。
一位开发者的主目录,连SSH密钥和密码库一起被传走
报告发布后,开发者 "A Green Being" 在 X 上发帖称,自己因为不小心在用户主目录下运行了 Grok Build,导致整个 home 目录——包括 SSH 密钥、密码管理器数据库、文档、照片、视频——被上传到了 xAI 的服务器。
这条个案很快得到了独立印证。一位化名 wetlink 的用户在 Cereblab 的 gist 评论区留言,说自己在第二台机器上复现了同样的行为(同一版本号、同一 SHA-256 校验值),本地日志 ~/.grok/logs/unified.jsonl 里记录了 339 次 repo_state.upload.enqueued 事件,涉及四个不同仓库,其中一次会话的 repo_path 字段直接就是用户的整个主目录——而这一切都发生在任何缓解措施上线之前。
Git 历史本身放大了风险:仓库当前的工作区里就算已经删掉了敏感信息,只要没有清理提交记录,几个月前提交过的密钥、内部服务器地址、未公开功能代码,依然会随着完整的 git bundle 一起被打包带走。
同类工具里,没有第二家这么干
Cereblab 用同样的方法测试了 Claude Code、Codex 和 Gemini,得到的结果是三家都"老实":只发送模型实际打开过的文件,植入的"从未读取"哨兵文件从没离开过本机。Gemini 在真实任务测试中因为触发了配额限制没能跑完全程,但在空闲状态测试里同样没有观察到仓库级上传。Cereblab 在给 Cybernews 的回应里写道:"另外三家都留在本地,只发送它们真正打开过的文件,我埋的那个'从不读取'文件从来没离开过我的机器。"
服务器悄悄关了开关,马斯克的表态却自相矛盾
舆论发酵之后,xAI 没有先发正式公告,而是先在服务端把 disable_codebase_upload 设成了 true——Cereblab 连续复测六次,都没再看到 /v1/storage 端的仓库上传。
几个小时后,SpaceXAI(xAI 现用名)的官方 X 账号才发文回应,强调"我们非常重视你的隐私,尊重客户的选择",并解释启用零数据留存(ZDR)的团队"任何跟踪与代码数据都不会被留存",未启用 ZDR 的用户可以运行 /privacy 命令查看或修改设置,同时删除此前已同步的数据。公司员工 Andrew Milich 和 Jason Ginsberg 相继在评论区安抚开发者情绪,随后 Elon Musk 亲自下场,用他标志性的单词"true"接话,并承诺:"作为预防措施,此前上传给 SpaceXAI 的所有用户数据都将被彻底、完全删除,不会留下任何东西。"
但同一天,Musk 在另一条独立的推文里却鼓励用户"继续分享数据",理由是保留"一些"数据有助于调试——这跟他自己刚做出的"彻底删除"承诺直接对不上。Cereblab 对官方的解释也不买账,公开反驳说 /privacy 从来不是真正解决问题的那个开关,公司不该把功劳记在一个实际上不起作用的用户设置上。
开关还留着,只是暂时按下了暂停
Cereblab 在 gist 里补充说明,这次"修复"是纯粹的服务端远程开关,不是重新发布了一版客户端。这意味着 xAI 完全可以在不发布任何新版本、开发者毫无察觉的情况下,随时把这个开关重新打开。gist 评论区的复现者 wetlink 在 7 月 13 日的探测显示,服务端返回的 in_remote_trace_upload_enabled 已经是 false,上传暂时被挂起,但客户端代码本身仍然保留着这套能力——他因此建议想要彻底关闭的用户,在本地配置文件里手动加上 [harness] disable_codebase_upload = true 这道"硬闸门",而不要只依赖服务端那个随时可能被拨回去的远程标志位。
The Register 和 Axios 都明确写道,他们无法独立核实 SpaceXAI 是否真的已经、以及何时完成了 Musk 承诺的数据删除。Axios 还补充了一段行业背景:这不是 2026 年第一次有 AI 公司因为数据留存政策惹上争议——今年早些时候,Anthropic 推出 Fable 5 和 Mythos 5 时,也曾在与部分企业客户存在零数据留存协议的情况下,开始为这两款模型保留用户数据 30 天,理由是"支持我们的安全工作"。Cybernews 还提到一个额外的现实分量:Grok 目前正被美国政府使用,并已获准接入五角大楼的机密网络——这为"默认上传整仓库"这件事平添了一层不容忽视的风险等级,尽管目前没有报道显示这次事件与该项接入之间存在直接关联。
用过 Grok Build,现在该做什么
如果在 xAI 服务端修复之前用 Grok Build 处理过私有或敏感仓库,应当把这次事件当成一次潜在的第三方数据外泄来对待,而不是简单关掉软件了事:
- 排查范围不能只看当前工作区,要把完整的 Git 提交历史也搜一遍,重点找云服务密钥、数据库连接串、包注册表令牌、签名材料、OAuth 凭据、Webhook 密钥;
- 只要某个凭据曾经出现在可读的仓库里,或者被某次会话读取过,就该直接吊销并更换,而不是新增一个了事——新建密钥不会让旧的失效;
- 检查云平台、代码托管、数据库、CI/CD 等下游系统的访问日志,确认这段时间里有没有出现异常调用;
- 不要只盯着
.env——任何格式的密钥、证书、内部主机名,只要在仓库历史里出现过,都要按"可能已经离开过这台机器"来处理。
Cereblab 的建议更直接:任何在事件曝光前用真实仓库跑过 Grok Build 的人,都应该默认仓库里的密钥"可能"已经被传走,主动去轮换 API Key、数据库密码、云令牌、签名密钥和 Webhook 凭据。至于那些已经躺在 grok-code-session-traces 存储桶里的数据到底删没删干净,除了等 xAI 自己给出可验证的答案,眼下没有第三方能替开发者去核实。