7月12日、cereblabが「What xAI Grok Build CLI actually sends to xAI - a wire-level analysis (grok 0.2.93) · GitHub」と題した記事を公開した。xAIの公式コーディングCLI「Grok Build」が実際に外部へ送信するデータの範囲を、TLS通信傍受(mitmproxy経由のHTTPS復号)と再現可能な証拠ファイルで調査した報告だ。
最も目を引く数字がある。AIモデルが受け取ったデータは192KBだったのに対し、別チャネルでストレージへ送信されたデータは5.10GiB——約27,800倍の差だ。しかも「ファイルを一切読むな」と明示的に指示したセッションでも、リポジトリ全体(gitの全履歴を含む)が漏れなくアップロードされ、すべてHTTP 200で受理されていた。AI系コーディングツールのデータ送信範囲はGitHub CopilotやCursorをめぐっても繰り返し議論されてきたが、今回の調査はその問いに実測データで答えるものだ。
確認された3つの動作
Grok CLIのバージョン0.2.93を通常のコンシューマーアカウントで使用したところ、以下の3点が確認された。
.envファイルを含むファイルの内容が、無加工・無削除のままxAIへ送信され、HTTP 200で受理される- エージェントが実際に「読んだ」ファイルとは独立に、リポジトリ全体(gitの履歴を含む)がアップロードされる
- アップロード先はGoogle Cloud Storageのバケット
grok-code-session-tracesであり、「モデル改善のためのデータ収集を無効化」する設定を変更しても止まらない
いずれもmitmproxyを使ったHTTPS通信傍受、バイナリのstrings解析、ステージングされたアーティファクトの直接検査、SHA-256付きキャプチャファイルという再現可能な証拠に基づいている。
解析手法:mitmproxyでTLSを復号
環境はmacOS(Apple Silicon)、grok 0.2.93、2026年7月。mitmproxyのCA証明書をloginキーチェーンに信頼登録し、Grokをプロキシ経由で起動するだけでよい。
security add-trusted-cert -r trustRoot -k ~/Library/Keychains/login.keychain-db \
~/.mitmproxy/mitmproxy-ca-cert.pem
HTTPS_PROXY=http://127.0.0.1:8080 SSL_CERT_FILE=~/.mitmproxy/mitmproxy-ca-cert.pem \
grok -p "<prompt>" --cwd <repo>
重要なのは、Grokがmitmproxyの証明書をピン留め(certificate pinning)していない点だ。つまりユーザーが自分のトラフィックをそのまま復号できる。
検証にはcanaryリポジトリを用いた。各ファイルに一意のマーカー文字列を埋め込み、どのデータが送信されたかをトークン単位で追跡できるようにする手法だ(セキュリティ研究で広く使われる)。
API_KEY=CANARY7F3A9-SECRET-should-not-leave
DB_PASSWORD=CANARY7F3A9-DBPASS
Finding 1:.envの中身がそのままxAIへ届く
GrokがファイルをAIに渡す際、内容は2つのチャネルで送信される。
- **チャネルA:
POST /v1/responses**(モデルへのリクエスト本文、48,070バイト)にファイル内容が埋め込まれる - **チャネルB:
POST /v1/storage**にアーカイブとして保存され、HTTP 200で受理される
復号された/v1/responsesリクエスト本文には、canaryのAPIキーとDBパスワードがそのまま含まれていた。さらに、session_stateアーカイブをステージングエリア(~/.grok/upload_queue/)から取り出して展開すると、同じcanary値が全件復元できる。
「エージェントが読んだファイルだけでしょ」という反論については、「一切ファイルを開かずOKとだけ返して」と指示したコントロール実験で検証している。この場合、未読ファイルのcanary値はチャネルAには現れなかった——チャネルAに限れば、エージェントが実際に読んだファイルのみが送られる。だがチャネルBはその限りではない。
Finding 2:12GBリポジトリから5.10GiBが全件HTTP 200で転送
「何も読むな」と指示したセッションでも、別チャネルでリポジトリ全体がアップロードされることが確認された。決定的なのはバイト量の乖離だ。
| チャネル | 転送量 |
|---|---|
チャネルA /v1/responses(モデル入力) |
192 KB(5リクエスト) |
チャネルB /v1/storage(ストレージ) |
5.10 GiB |
モデルが受け取ったのは192KBにすぎないのに、ストレージ側には5.10GiBが送信されており、しかも全リクエストがHTTP 200(失敗ゼロ)だ。約27,800倍の差がある。
実際のリポジトリ(cereblab_api)に対して「OK以外返すな、ファイルを開くな」と指示した上でキャプチャしたところ、gitバンドル形式(git bundleコマンドで生成されるリポジトリの完全なアーカイブ)でリポジトリ全体がアップロードされていた。このバンドルをgit cloneすると、絶対に開かれないはずのファイルsrc/_probe/never_read_canary.txtが中身ごと復元される。gitの全履歴(4コミット、47ファイル)も含まれていた。同様の実験を別リポジトリ(cereblab_auth)でも再現し、同じ結果を得ている。
Finding 3:送信先はGCSバケット、設定変更では止められない
バイナリをstringsで解析すると、送信先が明示されている。
crates/codegen/xai-data-collector/src/gcs.rs
grok-code-session-traces
storage.googleapis.com
"Uploading bytes to GCS via proxy"
キャプチャされたmetadata.jsonにはgs://grok-code-session-traces/…というパスが含まれており、Google Cloud Storageが使われていることが確認された。
ここで特に注意すべきなのは、xAIのUI上で「モデル改善のためのデータ使用を無効化」しても、/v1/settingsのレスポンスにはtrace_upload_enabled: trueが返り続ける点だ。この設定変更はアップロードを止めない——ユーザーが「オフにした」と思っている操作が実際には機能していない。
「学習に使われているか」は未証明
cereblabは記事内で重要な留保を記している。送信・受理・保存は証明されたが、xAIがそのデータでモデルを学習させているかどうかはポリシーの問題であり、今回の解析では証明していないという点だ。証明されているのはデータが「出ていく」事実のみだ。
また以下の点も未検証として明示されている。
.gitignoreされたファイルが送られるかどうかsk-…形式など高エントロピーな実際のAPIキーに対してリダクション処理が存在するかどうか
xAIからの公式声明や本調査への応答は、記事公開時点では確認されていない。xAIの利用規約およびプライバシーポリシーではデータ収集について言及されているが、CLIツールによるリポジトリ全体のアップロードがその範囲として明示されているかどうかは、ユーザー自身が確認する必要がある。
詳細はWhat xAI Grok Build CLI actually sends to xAI - a wire-level analysis (grok 0.2.93) · GitHubを参照していただきたい。