你只是让它回个 OK,Grok 把整个仓库连 .env 一起传走了
先说结论,免得你划走了错过重点。
有个叫 cereblab 的人,拿 mitmproxy 把 xAI 官方那个编码 CLI(就是 grok,Grok Build 那套)的网络流量整个抓下来看了一遍。测试方法很轴,也很干净,自己的机器、自己的流量、仓库里全塞假密钥。然后他抓到了三件让我看完盯着屏幕沉默了几秒的事。
第一,你读过的文件,包括 .env,原文不打码地传给了 xAI。第二,就算你明确跟它说「只回复 OK,别读任何文件」,它照样把你整个仓库打成一个 git bundle 传走了,连 git 历史一起。第三,你去设置里把「改进模型」那个开关关掉,没用,仓库照传不误。
好家伙。我看到第二条的时候,第一反应是不信。让它别读文件它还传?那这个「别读」到底是拦谁的?
咱一条一条聊。这篇我不想写成又一篇「xAI 翻车实锤」的通稿,那种满网都是。我想聊的是另一件更值得你花十分钟的事,我从这份流量分析里看到的,不是 Grok 一家的问题,而是一整类工具的默认行为,只是这次被人用抓包仪照出来了而已。
抓包这事,为什么可信
先说为什么我信这份报告,而不是当成又一个 X 上的爆料。
因为他用的是最笨也最硬的办法,看线缆。不是看 CLI 打印了什么,不是猜,是在你的机器和 xAI 服务器之间架了个 mitmproxy(一个能拦截、解密 HTTPS 流量的中间人代理,做安全测试的标配工具),把每一个请求的方法、地址、状态码、字节数全记下来,请求体也存盘。
这里有个细节我得夸一句,Grok 的二进制没做证书固定(certificate pinning,一种防止流量被中间人代理解密的机制)。做了 pinning 的 app,你架代理它直接拒绝连接,抓不到。Grok 没做,所以这份流量能被完整解开看。对做逆向的人来说这是方便,对 xAI 来说,这说明他们压根没设防这种审计。
然后他还很克制。报告里专门有一节叫「我没有证明的事」,白纸黑字写着,我只证明了数据被传输、被接受、被存储,我没有证明 xAI 拿去训练了。上传不等于训练,那是政策问题。
这种态度,比那些「实锤 xAI 偷你代码训练模型!!!」的标题党可信一百倍。一个愿意主动划清自己证据边界的人,说出来的话我才敢往心里放。
第一件事,你的 .env 是怎么走的
咱先看最直观的,密钥泄露。
他在测试仓库里放了个假的 .env,里面写着 API_KEY=CANARY7F3A9-SECRET-should-not-leave,翻译成人话就是「这玩意不该离开我的机器」。这种带唯一标记的假密钥叫 canary(金丝雀),矿工下矿带金丝雀探毒气那个意思,一旦它出现在不该出现的地方,就说明泄露了。
结果呢,抓到一个 48KB 的请求体,发往 POST /v1/responses,也就是模型对话那条通道,里面 canary 原文躺着,一个字符都没打码。六个文件的标记全找回来了,源码、README、嵌套的 JS、API key、数据库密码,全在。
到这一步,其实还不算特别刺激。你可能会说,那不废话吗,AI 编码工具要帮你干活,肯定得把你代码发给模型看啊,云端 agent 都这样。
对,这话没错,作者自己也承认了。任何云端编码 agent 都得把代码传上去,不然模型看不见怎么帮你写。这不是新闻。
真正扎眼的是那个词,不脱敏。.env 是什么?是行业里约定俗成专门放密钥的文件,几乎所有框架、所有部署工具都知道 .env 要 gitignore、要单独保管。这是个有强烈语义的文件名,一个负责任的工具在往外传之前,理应对它多看一眼,哪怕做个最粗糙的关键词过滤。Grok 没有,跟传一个 README 一模一样的待遇,原文糊上去就发。
我做工具链这行,特别能理解这种「顺手」是怎么来的。你写文件读取模块的时候,脑子里想的是「把 agent 需要的上下文喂给模型」,.env 也是文件,也可能跟任务相关(比如它要帮你调环境变量),那就读呗。脱敏是另一个人、另一个 PR、另一个 sprint 的事,而且往往是出事之后才补的。这不是 Grok 团队坏,是这个行业默认把「先跑起来」排在「先别泄露」前面。
但理解归理解,一码归一码。你的密钥明文出了你的机器,这件事本身就是个洞,跟你能不能理解开发者的心路没关系。

第二件事,这才是我后背发凉的地方
如果说 .env 明文还能用「你确实让它读了代码」来解释,那第二件事就彻底没法洗了。
作者做了个对照实验。提示词写得清清楚楚,Reply with exactly: OK. Do not read or open any files。只回 OK,别读任何文件。他还在仓库里埋了个文件 src/_probe/never_read_canary.txt,里面塞了个独一无二的标记 CANARY-XR47P2-NEVERREAD-UNIQUE,这文件从头到尾没让 Grok 碰过。
然后 Grok 干了什么?它把整个仓库打成一个 git bundle,通过 POST /v1/storage 传走了,返回 200,服务器收下了。
git bundle 是啥?就是 git bundle create 能生成的那种单文件仓库快照,把所有被跟踪的文件加上完整提交历史打成一个包,拿去 git clone 能原样还原整个仓库。作者就这么干了,clone 出来,那个从没被读过的 never_read_canary.txt,标记原文在里面。连着 4 个 commit、47 个文件的完整 git 历史,一起走了。

我得停在这儿多说一句,因为这个区别太关键了。
「模型读了你的代码去回答问题」和「有个后台进程把你整个仓库连历史打包上传」是两件完全不同的事。前者是功能,你用它就得接受。后者是……我不知道该叫它什么,它跟你这一轮问答干的活儿没有任何关系,你让它闭嘴它也传。
而且这不是小打小闹。作者做了个尺寸扫描,从 64MB 一路测到 12GB。12GB 那个仓库全是随机生成的、从来没被读过的文件,结果模型对话通道只走了 192KB,而仓库上传通道走了 5.10 GiB,抓包是中途手动停的,停的时候还在涨。
192KB 对 5.10GiB,差了将近两万八千倍。

这个数字是整篇报告里最有说服力的一击。它像一把手术刀,把两条通道彻底切开了,模型根本没读那些文件(192KB 装不下 5GB 的内容),但 5GB 还是走了。那走的是什么?只能是整仓快照。上传量跟着仓库大小走,跟你读没读、问没问,一点关系都没有。
厉害了,这个设计。你甚至能从扒出来的二进制字符串里想象出他们内部那个 xai-data-collector crate 长什么样,一个独立的后台协调进程,跟主对话流程解耦,你在前台跟模型聊天,它在后台默默 collecting workspace files、spawning background coordinator。从工程角度看,甚至可以说设计得挺利索。从用户角度看,这就是在你眼皮底下把家底搬空。
第三件事,那个开关是个摆设
到这儿你可能想,行,那我去关掉数据收集不就完了。grok.com 设置里不是有个「Improve the model」(改进模型)的开关吗,关了它。
作者也是这么想的。他把开关关掉,重跑。
Grok 还是把整个仓库打成 git bundle 传走了,200。服务器返回给 CLI 的设置里,trace_upload_enabled 依然是 true,upload_enabled 依然是 true。
这个开关管的是训练,不管上传。你关掉它,意思是「别拿我的数据训模型」,但你的代码该传到那个 GCS 桶还是照传。退出训练,不等于阻止你的仓库离开机器。这两件事在产品里被绑在了一个开关的印象里,但在代码里是分开的。
我盯着这段看了挺久。因为这是最典型的那种,技术上无懈可击、体感上纯纯欺骗的设计。你没撒谎,开关确实管训练,你也确实给了退出选项。但一个正常用户看到「改进模型」的开关关了,合理预期就是「我的东西不外传了」。结果代码还在传,只是不训练。这中间的落差,就是所谓的 dark pattern(暗黑模式,用设计误导用户做出违背自身利益选择的套路)。
顺便说个更实际的破事,作者还发现这个上传队列 ~/.grok/upload_queue 每轮暂存大概 3GB 快照,高负载下能涨到几十 GB 把你磁盘吃满。这已经不是隐私问题了,这是个实打实的 bug,你本地磁盘替它的数据收集买单。
那存到哪去了
流量最后落在 Google Cloud Storage 的一个桶里,名字叫 grok-code-session-traces。这名字在二进制字符串里、在抓到的 metadata.json 里都是原文出现的,每个文件的目的地长这样,gs://grok-code-session-traces/repo_changes_dedup/v2/...。
不是 AWS S3,是 GCS。二进制里确实链了 aws-sdk 作备用路径,但实际命名和上线的目的地是 Google 的桶。另外还有 Mixpanel 的遥测(api.mixpanel.com/track),这个倒是常规操作,很多工具都埋。
session-traces,会话轨迹。你的代码库,被叫做会话轨迹,存进了一个以「轨迹」命名的桶里。这个命名本身就挺说明问题的,它不是被当成「你的私有资产临时借用一下」,而是被当成「会话产生的可留存数据」。
所以这是 Grok 独家吗
这是我最想跟你掰扯的一层,也是我觉得比「Grok 又翻车」重要得多的一层。
我的判断是,不是独家。.env 明文这个具体的锅,Grok 得背,别人不一定这么糙。但「把整个工作区往服务器传」这件事,是这一整代云端编码 agent 的默认体质,只是大家做的程度、透明度、以及有没有被人拿抓包仪照出来,不一样。
你想想这类工具的工作原理就明白了。它要理解你的项目,就得有项目上下文,光靠你聊天里贴的几行代码远远不够。所以它需要索引你的代码库、需要知道文件结构、需要在你不主动贴的时候也能去翻。这个「翻」的能力,往上一步就是「传」,往上两步就是「留」。功能和越界之间,隔的不是技术边界,是产品团队的自觉和监管的红线。
而这两样东西,现在都还没到位。
所以我不打算把这篇写成「快卸载 Grok」。卸载 Grok 你就安全了吗?你用的那个别的云端编码工具,你确定它没干类似的事?你只是没看见而已。cereblab 这份报告真正的价值,不是抓了 Grok 一个现行,是给所有人提了个醒,你敲进 AI 编码工具的每一行代码、每一个配置文件,都得默认它可能会离开你的机器。这个假设一旦立起来,你的行为就会变。
今天就能做的自查清单
聊了这么多,落到地上。不管你用的是 Grok、Cursor、Claude Code、Codex 还是别的什么,下面这几件事今天就能做,成本不高,但能把你最要命的那部分风险摁下去。
一,把密钥挪出代码库。这是最重要的一条,别的都可以往后排,这条不行。.env 别放在项目目录里,用系统级的密钥管理,macOS 的 keychain、1Password 的 CLI、云厂商的 secrets manager 都行。让 AI 工具在你的仓库里根本翻不到明文密钥,它想传都没得传。
二,给敏感项目建物理隔离。真正涉密的代码,涉及生产密钥、客户数据、未公开业务逻辑的,别在装了云端编码 agent 的机器 / 目录上开。要么用本地模型(ollama 跑个 DeepSeek 或者 Qwen,代码一步都不出机器),要么就老老实实手敲。方便和安全这次真没法全拿。
三,看一眼你的工具往哪发数据。你不用真去架 mitmproxy,那门槛高。但 macOS 上有 Little Snitch、Lulu 这种出站防火墙,装上之后哪个进程往哪个域名发包一目了然。你会惊讶于你那些「本地工具」到底连了多少家的服务器。
四,别信任何一个叫「改进模型」的开关能保护你的隐私。它大概率只管训练那一档。真想知道数据传没传,只有看流量,不看开关。开关是给你心理安慰的,流量才是事实。
五,.gitignore 该写写全,但别把它当护城河。这份报告里有个细节,作者没测 gitignore 的文件会不会被上传,因为那个上传机制是 git bundle,理论上 bundle 只打包 git 跟踪的文件,所以 gitignore 的东西可能逃过一劫。但这只是「可能」,没被证明。你可以写全 gitignore 作为第一道防线,但别指望它拦住一个铁了心要打包整仓的后台进程。
写在最后
说真的,越看这波编码 agent 的浪潮,我越觉得最被低估的风险不是「AI 写的代码有 bug」,而是「AI 工具本身是个你看不透的黑盒」。
我们把越来越多的信任交给这些工具,交出去的时候是无意识的,收回来的时候得靠别人拿抓包仪一点点照。cereblab 这次照出来的,是 Grok。下一次不知道是谁,也不知道有没有人愿意花这个功夫去照。
我一直觉得,一个工具值不值得信,不看它宣传时说得多好,看它在你看不见的地方怎么做事。这份报告最狠的地方,就是把「看不见的地方」变成了看得见的字节数。5.10 GiB,一个你让它闭嘴、它却把你家底打包搬空的数字。
万青那句「是谁来自山川湖海,却囿于昼夜厨房与爱」,我今天想歪一句,我们来自开源自由的山川湖海,却把代码囿于一个个看不见的云端桶里。
这不是让你恐慌到卸载所有工具。恐慌没用,该用还得用,生产力摆在那儿。是让你从今天起,敲每一行代码之前,脑子里多一个默认假设,这台机器上跑着的东西,可能都在偷偷往外看。假设立起来了,前面那五条自查你自然就会去做。
工具是租来的,代码是自己的。这道理,值得你花十分钟记住。