有研究者给 xAI 的官方编程 CLI Grok Build(版本 0.2.93)下了一道明确指令:“回复 OK,不要读取任何文件。”按理说,模型不读文件,数据就不该离开本地。结果抓包一看,整个代码仓库——包括一个从没被打开过的密钥文件、外加完整的 git 历史——原样打包成 git bundle,发到了 xAI 的服务器上,状态码 200。
这不是模型多读了一行代码的小事故,而是一套跟“模型看没看”完全脱钩的上传机制。
发生了什么
研究者用 mitmproxy 截获了 Grok CLI 的全部网络请求,做了三组实验:
- 一个真实仓库里放一个从未被要求读取的密钥文件,结果它连同全仓库历史被打包上传,git clone 出来密钥原样可见。
- 一个 12GB、全部由随机文件组成、模型从未读取过的仓库,存储通道
/v1/storage传出了 5.10 GiB,全部 HTTP 200;同一时间模型对话通道/v1/responses只传了 192 KB。 - 两者相差约 27800 倍——模型不可能在 192KB 里装下 5GB 的内容,说明这 5GB 走的是另一条完全独立的通道。
目的地是 Google Cloud Storage 的 grok-code-session-traces 桶,不是常见的 AWS S3。二进制文件里能直接 strings 出这套机制的源码路径,来自一个叫 xai-data-collector 的 Rust 组件,说明这不是意外泄漏,而是产品里内建的一等功能。
文档说的是一套,实测跑的是另一套
xAI 的企业部署文档反复强调本地会话存到 ~/.grok/,企业客户可以开 Zero Data Retention(ZDR),开了之后 prompt 和代码不在推理层持久化,还提供 grok sessions delete、grok memory clear 这样的清理命令。整套叙事的重点是“你的数据可控、可删、可本地化”。
但检索 xAI 全部公开文档,找不到任何一处提到 /v1/storage 接口或 grok-code-session-traces 这个存储桶。文档讲的是消费者能感知的那一层:本地文件夹、删除命令、隐私开关。实测抓到的,是文档完全没覆盖的另一层:一条默认开启、把整仓库连同 git 历史打包上传的后台通道。
- 风险.这套机制在普通消费者账号(非企业 ZDR)下默认打开,用户没有对应的关闭入口。
更值得记一笔的细节是,把 /v1/settings 里“Improve the model”那个训练开关关掉之后,接口返回的 trace_upload_enabled 依然是 true。也就是说,那个用户以为能管住数据去向的开关,其实只管训练,不管上传。
三家 CLI,姿态不是一回事
同样是要读代码库的编程 CLI,几家公司在“默认要不要传整个仓库”这件事上的官方表态并不一样。OpenAI 的 Codex CLI 文档明确说默认只发 prompt、上下文摘要和可选的 diff,不整体上传源码;Anthropic 的 Claude Code 文档只说明使用需要联网做 AI processing,没有做“不整仓库上传”的明确承诺,消费级账号删除对话后 30 天内彻底清除,商业客户可申请 ZDR;Grok Build 这边,文档同样语焉不详,而实测证据直接指向“整仓库默认上传”。
这份证据链本身也有它没做到的地方,得说清楚:它证明了数据被“传输、接受、存储”,没有证明 xAI 拿这些数据去训练模型——训练与否是另一个问题,目前没有证据支持,也没有证据排除。研究者只在 macOS、0.2.93 这一个版本上测试,其他平台和版本是否一样,不知道。这套结果目前只有作者本人的抓包和 Reddit 上一条未经证实的复现讨论,xAI 官方还没有回应。
但“数据合规靠开关”这个默认心智,已经被这次测试戳穿了一角。孔子讲“听其言而观其行”,放在这里正合适:开关和文档是“言”,抓包抓到的字节数才是“行”。对拿真实代码库跑 Grok Build 的团队来说,眼下最现实的动作是:密钥文件不要留在被 CLI 扫到的目录里,或者干脆先等 xAI 把 /v1/storage 这套机制写进文档、给出关闭方式,再决定要不要把私有仓库交给它。
开关管的是训练,不管的是上传,这中间那道缝,才是真问题。
接下来该盯的,是 xAI 会不会正面回应这份抓包结果,会不会给消费级账号补一个真正能关掉整仓库上传的选项。在那之前,“已开启 ZDR”和“已关闭训练”都不等于“数据没有离开你的机器”。
