xAI Grok Build CLIがxAIへ送信するデータ:ワイヤレベル分析
(gist.github.com/cereblab)grok 0.2.93のネットワークトラフィックを直接キャプチャした結果、Grok Buildは読み取ったファイルをマスキングなしで送信し、session_stateとして保存していたほか、テスト用の.env秘密値も2つの経路にそのまま含めていた- モデル要求でエージェントが読んだファイルを送るのとは別に、すべての追跡ファイルとGit履歴を含むリポジトリ全体がgit bundleとしてアップロードされ、開かないよう指定したファイルも原文のまま復元できた
- 12GBのランダムファイルリポジトリでは、
/v1/responses要求は合計192KBだった一方、/v1/storageの送信量はキャプチャ中断時点までに5.10GiBに達し、約27,800倍の差があり、すべての保存要求がHTTP 200を返した - アップロード先はGoogle Cloud Storageの**
grok-code-session-tracesバケット**で、「Improve the model」をオフにしてもtrace_upload_enabled: trueとupload_enabled: trueが維持され、リポジトリ全体のアップロードが継続した - 実験はデータの送信・受理・保存を立証するが、モデル学習に使われたかどうかは確認できておらず、
.gitignoreファイルやすべてのアカウント・設定の組み合わせも試していないため、結果は2026年7月の特定バージョンに限られる
テスト対象と分析範囲
- 対象は一般消費者アカウントでログインしたxAI公式のGrok Build CLI
- インストール先は
~/.grok/bin/grok - ブラウザでXまたはSuperGrokアカウントを使って認証し、APIキーは使用しない
- テストしたバイナリはApple Silicon向けの
grok 0.2.93 (f00f96316d4b) - SHA-256は
2a97ba675bd992aa9b981e2e83776460d94f469b510c0b8efe28b50d236d767c
- インストール先は
- バイナリ文字列から独自のRustアップロード構成要素とストレージ関連の定数を確認
crates/codegen/xai-data-collector/src/gcs.rsstorage_client.rs,queue.rs,file_access_tracker.rs,circuit_breaker_observer.rsxai-grok-shell/src/upload/{gcs,turn,trace,manifest}.rsgrok-code-session-traces,storage.googleapis.com,Uploading bytes to GCS via proxy
- すべてのキャプチャはテスト実施者のコンピュータとそのトラフィックのみを対象とし、リポジトリには実際の資格情報の代わりに固有のcanary文字列を入れた
再現とトラフィックキャプチャ方法
- macOS Apple Siliconで
mitmproxyのCAをログインキーチェーンに信頼済み証明書として追加し、HTTPS_PROXYとSSL_CERT_FILEを設定してGrokのHTTPS要求をキャプチャした - Grokはこの構成で**証明書ピンニング(certificate pinning)**によるキャプチャ遮断を行わなかった
mitmdumpアドオンで要求ごとのメソッド、ホスト、パス、応答ステータス、要求バイト数を記録し、xAIホスト宛ての要求本文を保存した~/.grok/upload_queue/*を実行中にコピーし、gzipとtarで展開して送信待ちのステージングアーティファクトも検査した- 各ファイルには出所を識別できる固有マーカーを入れ、秘密ファイルには次のようなダミー値を使った
API_KEY=CANARY7F3A9-SECRET-should-not-leaveDB_PASSWORD=CANARY7F3A9-DBPASS
チャネルA:読み取ったファイルと.envのモデル要求送信
- Grokが読み取ったファイル内容は、
POST cli-chat-proxy.grok.com/v1/responsesのモデルターン本文にシリアライズされていた- キャプチャした48,070バイトの要求には
"model":"grok-4.5"とメッセージ配列が含まれていた .envのAPI_KEYとDB_PASSWORDのcanaryが改行まで含めて原文そのままで見つかった- ソース、ロジック、README、ネストされたJavaScriptファイルと2つの秘密値を含む6個のマーカーを要求本文から復元できた
- キャプチャした48,070バイトの要求には
- 同じ内容は
session_stateアーカイブにも入り、POST /v1/storageでアップロードされ、その保存要求はHTTP 200で受理された- 圧縮アーカイブを展開して調べると、2つの秘密値と他のファイルマーカーがすべて見つかった
- 秘密値はモデルが一時的に処理するだけでなく、保存用アーカイブにも記録されていた
Reply exactly OK, do not read any filesという統制プロンプトでは、開いていないuntouched_secret.txtのマーカーは/v1/responses本文に現れなかった- これはモデル要求チャネルがエージェントが実際に読んだファイルを中心に構成されている範囲だけを示す
- 別のリポジトリ全体スナップショットチャネルには未読ファイルも含まれるため、この統制結果はリポジトリ全体アップロードを否定しない
- 秘密値は
.envまたはsecrets.envにあり、Gitで追跡されるファイルだった.gitignoreに含まれるファイルもアップロードされるかどうかは別途試していない- canaryは実際の高エントロピートークン形式ではないため、
sk-…のような特定形式向けの別個のマスキング機構がまったくないとまでは立証しない - 確認できた範囲は、テストした
.envの内容がマスキングされずに送信されたという点
チャネルB:未読ファイルまで含むリポジトリ全体のアップロード
/v1/storageは、モデルが読んだファイルを送る/v1/responsesとは別にワークスペース全体のスナップショットをアップロードしていた- 実際の
cereblab_apiリポジトリでReply with exactly: OK. Do not read or open any files.と指示したうえで実行した- Grokは
POST /v1/storageで152,102バイトのgit bundleを送信し、サーバーはHTTP 200を返した - キャプチャした
uploaded_repo.bundleをgit cloneすると、開かないよう指定したsrc/_probe/never_read_canary.txtが復元できた - ファイル内の
CANARY-XR47P2-NEVERREAD-UNIQUEマーカーも原文そのまま残っていた - bundleには4件のコミット、47個のファイルと完全なGit履歴が含まれていた
- Grokは
- 2つ目の独立リポジトリ
cereblab_authでも同じ結果が再現されたPOST /v1/storageで31,743バイトのgit bundleがアップロードされ、HTTP 200を返した- 復元したリポジトリで、未読ファイルの
CANARY-AUTH-4T8K2-NEVERREADマーカーがそのまま確認できた
- 別の298ファイル規模の
cereblab_api実行では、ステージングされたmanifestが未読ファイルまで列挙していたsrc/_probe/never_read_canary.txtとsrc/apns.ts,src/index.tsなどの実際のソースが一緒に記録されていたmetadata.jsonのファイルごとのfileIdはgs://grok-code-session-traces/repo_changes_dedup/v2/supplemental/sha256_…形式だった- この実行ではクォータに阻まれてコンテンツblob送信までは完了しなかったが、別のgit bundle実験と12GB実験で実際のアップロードが確認された
- バイナリには
after_codebase.tar.gz,repo_state.upload,collecting workspace files,spawning background coordinatorといった文字列も含まれており、ワークスペース収集メカニズムと一致していた
12GBリポジトリで確認した複数GiBの送信
- 圧縮されにくいランダムファイルでリポジトリサイズを増やしながらアップロード挙動を観察した
- 64MBでは約48MBの単一
POST /v1/storageがHTTP 200を返したが、ログは保存できなかった - 約600MBでは約7.5MBチャンクが数十個すべてHTTP 200を返したが、ログは残っていない
- 約3GBではmultipart初期化後に
storage.googleapis.comへ50MB単位のPUTが観察されたが、直接PUTのログは保存できなかった - 約12GBでは約75MBチャンクを
/v1/storageへ継続送信し、5.10GiB時点でキャプチャを中断した
- 64MBでは約48MBの単一
- 保存された
wire_12gb.logには/v1/storage*のHTTP 200応答が合計83件ある- コンテンツアップロード
POST /v1/storageが82件 - このうち約75MBチャンクが73件
- 73件のチャンク合計は
5,476,083,317B、すなわち5.10GiBまたは5.48GB - 小さいPOST 9件と
/v1/storage/batch_existsの重複確認1件も含まれる /v1/storage*要求の総バイト数は5,476,228,005Bで、失敗した保存要求はなかった
- コンテンツアップロード
- キャプチャはアップロード継続中に中断したため、12GB全体のアップロード完了を立証するものではない
- 最後の記録も約75MBチャンクに対するHTTP 200だった
- 立証できる範囲は、5.10GiB以上が失敗なく送信され、中断時点でも増え続けていたということ
- 同じセッションの2チャネル間には大きな容量差があった
/v1/responsesは5件の要求を合計して196,705B、約192KBで、最大の要求でも60,394Bだった/v1/storageは5.10GiBだった- 約27,800倍の差が生じ、モデル要求に含まれていない大量のリポジトリデータが別の保存チャネルへ移動していた
- モデルのクォータエラーと保存アップロードは独立して発生していた
/v1/responsesではHTTP 402が1回、429が3回発生した- セッションbookkeeping要求では無関係な404が1回発生した
- 最初の429の後も
/v1/storageのHTTP 200が76回続いた - 保存要求82件はすべて成功し、テスト範囲では保存容量制限エラーは発生しなかった
- この結果はローカル
upload_queueが空になる現象には依存しない- キューはアップロード成功時にも破棄時にも空になる可能性があるため、証拠として使っていない
- 根拠は、要求本文が実際にネットワークへ送出されHTTP 200を受けたキャプチャと、アップロードされたgit bundleからファイルを復元した結果である
保存先とテレメトリ
- 保存先はAWS S3ではなく、Google Cloud Storageの**
grok-code-session-tracesバケット**と確認された- バイナリに
grok-code-session-traces,storage.googleapis.com,Uploading bytes to GCS via proxyが含まれていた - 保存された
metadata.jsonにはファイルの宛先がgs://grok-code-session-traces/…として記録されていた - 約3GBの実験では当該GCSホストへの直接multipart PUTも観察したが、ログは保存できなかった
- バイナリに
aws-sdk-s3が含まれていても、テストで確認できた宛先はGCSだった
- バイナリに
- サードパーティおよび自社のテレメトリ要求も確認された
- Mixpanelの
api.mixpanel.com/trackと/engage grok.com/_data/v1/events- これらの要求はいずれもHTTP 200を返した
- Mixpanelの
- 確認したCLIインストールスクリプトとquickstart資料では、
repo_state,session_state,~/.grok/upload_queue,grok-code-session-tracesへのアップロードは見つからなかった- すべてのxAI文書とヘルプを調査したわけではないため、どこにも文書化されていないと断定はできない
- 確認可能な範囲は、CLI自身の設定資料では明示されていなかったということ
~/.grok/upload_queueは1ターンで約3GBのスナップショットをステージングでき、高負荷時には数十GBまで増えてディスクを使い切る可能性があった- これはアップロードのプライバシー問題とは別の信頼性の問題である
「Improve the model」設定とポリシー範囲
- クラウド型コーディングエージェントが作業に必要なコード文脈をサーバーへ送ること自体は必要な動作である
- テストで確認された挙動は次の3点に分けられる
.envのような秘密ファイルをマスキングなしで送信する- その内容を明示されたGCSバケットに保存する
- リポジトリ全体のアップロードが、確認したCLI設定資料に明示されないままデフォルトで有効になっている
- xAIの消費者向けポリシーは、モデル改善のためのデータ利用とopt-outを広く扱っており、Private Chatは自動opt-outで、opt-outは遡及適用されない
- 関連文書はxAI Privacy PolicyとConsumer ToS
- こうした一般的な学習ポリシーは、特定の
repo_stateおよびGCSアップロードパイプラインを文書化することと同義ではない
-
「Improve the model」をオフにしてもアップロードは止まらない
- 設定をオフにしてもリポジトリ全体がgit bundleとして
/v1/storageへアップロードされ、HTTP 200を返した git cloneで未読ファイルとGit履歴を復元できた- CLIが受け取った
/v1/settingsには"trace_upload_enabled": true,"upload_enabled": true,"session_registry_enabled": trueが維持されていた "max_upload_file_bytes": 1073741824として、ファイルごとの1GiB制限も返されていた- テスト結果では、opt-outは学習への利用可否を制御するが、リポジトリがコンピュータを離れてアップロード・保存される動作自体は遮断しなかった
- 設定をオフにしてもリポジトリ全体がgit bundleとして
立証していない点と証拠の限界
- ネットワークキャプチャだけでは、xAIがデータをモデル学習に使用する事実を立証できない
- 確認できた範囲は、送信、HTTP 200による受理、保存用アーカイブとGCS宛先である
- 3GB実行で観察した
storage.googleapis.com/grok-code-session-tracesへの直接PUTログは上書きされて保存されなかった- 複数GiBアップロードの根拠は、保存された12GB実行の
/v1/storageログと、バケットを明示したバイナリおよびmetadataである
- 複数GiBアップロードの根拠は、保存された12GB実行の
- サイズ別試験のうち64MB、600MB、3GBのログは残っておらず、12GBログのみ保存されている
- 12GB実行は約5.10GiBで中断したため、12GB全体が最後までアップロードされると断定はできない
- すべてのアカウント種別と構成の組み合わせを試したわけではない
- 無料プランで複数GiBのアップロードが成功した
- SuperGrokでは「Improve the model」をオフにしてもgit bundleアップロードが成功した
- テストではアップロードをオフにする設定は見つからなかったが、どの環境でも絶対に無効化できないと断定はしない
- 当初はPID単位の
nettop結果を根拠に大容量blobがアップロードされていないと誤判断していたが、これは撤回する- 別個のアップロード調整プロセスと、GoogleのIPへ直接向かう事前署名付きPUTは、APIホストや単一PID基準の計測から漏れる可能性があった
- その後のプロキシによるワイヤキャプチャが初期判断に置き換わった
- 結果は
grok 0.2.93、macOS Apple Silicon、2026年7月の環境に限られ、xAIが後に挙動を変更する可能性がある
保存された主要証拠
secrets_responses_body.bin:.env原文が/v1/responses本文に含まれていたことを示すsecrets_session_state.tar.gz:同じ秘密値が/v1/storage用アーカイブに入っていたことを示すwire_12gb.log:5.10GiBの保存アップロード、83件の/v1/storage*HTTP 200、保存失敗0件と、2チャネル間の約27,800倍の容量差を記録model_limit.txt:モデル要求で発生した402が1回、429が3回であることを記録crate_strings.txt:xai-data-collector,grok-code-session-traces,storage.googleapis.comの文字列を保存uploaded_repo.bundle:アップロードされたgit bundleから未読ファイルと完全なGit履歴を復元した、最初のリポジトリ証拠uploaded_repo_auth.bundle:2つ目の独立リポジトリで同じ結果を再現した証拠staged_base_tree_manifest.json:未読ファイルがリポジトリスナップショットmanifestに列挙されていたことを示すstaged_metadata.json:ファイルの宛先がgs://grok-code-session-traces/…であることを示すgcs_puts.txtは直接GCS PUTを保存できなかったため空のプレースホルダーであり、そのPUTの保存証拠としては使えない
1件のコメント
Hacker Newsの意見
コーディングツールとLLMプロバイダーは常に分離し、bubblewrapサンドボックスでコーディングツールの権限を制限している
ツールは作業プロジェクトディレクトリだけを読めて、
.gitは読み取り専用、機密ディレクトリは空のディレクトリとしてマウントするネットワーク名前空間も分離し、UnixソケットのHTTPプロキシ経由でのみインターネットにアクセスさせ、特定のLLMプロバイダーホストだけを許可し、ツール自体のホストは遮断する
たとえばCrushには
*.openrouter.aiへのアクセスを許可するが、LLM一覧の自動更新に使われる*.charm.landはブロックする。おかげで**yoloモード**ですべての作業を任せやすくなるdebian:unstableのようなrootfsを取得し、別フォルダに完全なディストリビューション環境として構成するのがよいその中にAIエージェントをインストールし、ディストリビューションrootfsは読み取り専用、カスタムの
/home/userは読み書き可に設定してbwrapを実行するスクリプトを作ればよい。指定したディレクトリ外の重要ファイルは見えず、複数のエージェントを互いに見えないように実行することもできるさらに強化するなら、内部でgVisorの
runsc ... do ...を呼ぶか、muvmのような仮想マシンモニタを使える。bwrapは環境構成を担当させ、別のサンドボックスツールで封じる方式なので信頼しやすい設定が正しければ、
bwrapだけでもほとんどの攻撃者を防ぐには十分で、権限昇格には事実上Linuxカーネルのゼロデイが必要になる可能性が高い制約で防がなければならないほど愚かな振る舞いをするモデルなら、そもそも使う価値がないと思う。自分も環境を強化している最中で、慣行そのものを批判したいわけではない
claude-code、Codex、grok-buildのような独占的なネイティブコーディングエージェントランナーは、次のアップデートでどんな非公開機能が追加されるかわからず、プライバシーの面で危険だ
opencodeでAPI経由でモデルを使うほうがはるかに安全だが、ネイティブランナーほどの性能を出しにくいというトレードオフがある
Grokのやり方がより露骨なだけで、opencodeも実質的なセキュリティ境界を作れておらず、チートスを南京錠代わりに使うミームに近い
Windows XP SP1のリモートコード実行脆弱性のようなものを即座に修正しないのも危険だが、この数十年でアップデートしなかったために起きたであろう被害より、自動更新によって起きた被害のほうを多く見てきた
「エージェントが読んだファイルと無関係に、追跡中のすべてのファイル内容とGit履歴を含むリポジトリ全体をアップロードする」というのは非常に衝撃的だ
Elonが追いつくためにこういうことをするかもしれないとはある程度予想していたが、深刻に懸念している。価格競争力があり、grok-4.5の性能も十分高いが、まさにこういう理由で選ばなかった
CLIが誤ってSSHキーや他の機密情報を持ち出すかもしれず、プログラマーは実際こうしたミスをよく犯す。「アクセス可能なすべてのファイルをアップロード」が意図的なのかミスなのかに、安全を委ねたくない
最初の項目である「リポジトリ内の秘密情報ファイルをモデルが読んだ」は、事実上意図された動作だ
LLMはファイルを読む前に、その中に秘密情報があるかどうか判断できない。平文の秘密情報が入ったファイルへのLLMアクセス権を与えておいて、読んだと驚く側に根本的な問題がある
ただし、リポジトリ全体を自動でアップロードするのはばかげている。数GB規模のリポジトリなら回線によっては非常に長くかかるし、すべてのデータを集める別の目的がない限り、たいてい無意味に見える
エージェントを動かしている現在のワークスペースは、少なくともエージェントが自由に扱えるものだと常に想定してきたので、予想どおりの動作に見える
ほとんどのエージェントは最初のプロンプトでコードとその中の秘密情報まで読む。サーバー側でこれを活用してプロンプトの往復時間とツール呼び出しを減らせるなら、むしろユーザーに利益があるのではないかと思う
ところがここでは、プロジェクトフォルダ全体をGCPストレージバケットへ持ち出す別エンドポイントが見つかった。大規模分散システムを設計したことがある人なら、訓練データをかき集めるための構造だとわかるはずだ
すべてのファイルをアップロードしなくても、検索で関連部分だけを見つけてモデルが使えるよう送信できる
概要を人間が書いていればよかったが、内容自体は不安になる
AIが書いた報告は読むのがあまりにつらく、10秒ほど流し見しただけで興味を失った
盗んだ内容が「すべてのビジネスを自動化」するMacrohardプロジェクトや「everything app」に入るのか気になる
何でも自分で作る必要はなく、盗めばいいという発想に見える
倫理観のないままこういう会社を運営するなら、詐取の規模が明るみに出て規制で止められる前に、できるだけ多く盗もうとするはずだ。実際にそうしていると言っているわけではないが、経済的インセンティブはまさにその方向に揃っている
AIエージェントは、実行プログラムが起動されたディレクトリ内のファイルを読めると考えるべき
ほとんどは最初のプロンプトでコードとその中の秘密情報まで読み、
.envはローカル環境用なので実際の秘密情報を入れてはいけない。AIエージェントの指示は信頼できないため、本物の秘密情報とは隔離すべきこの前提を受け入れるなら、毎回コードをコンテキストとして送るよりサーバーに置いておくほうがよいかもしれない
このように別途アップロードする理由は、Muskが次のモデルのためにプロジェクト構成、人気ライブラリ、CIワークフローのようなクリーンな学習データを確保したいだけだと見ている
どのプロバイダーにも、結果改善に役立つなら同じことをする能力と動機がある
本当の違いは、
.envのような秘密情報ファイルをマスクせずに送信し、一時的に処理するだけでなく名前付きのGCSバケットに保存し、CLI設定ドキュメントでアップロード方式を知らせないままデフォルトで有効化していたことアクセス可能なパスに暗号化されていない
.envを置くべきではない。Grokが秘密情報を識別して無視してくれるほうが望ましいが、ユーザーがそのような動作に依存すべきではない「Improve the model」設定をオンにしていてもオフにしていても、リポジトリ全体が同じようにアップロードされる点は非常に深刻
ほとんどのAI企業も、データ収集に同意すれば自社の実行環境で似たことをするだろうが、明示的にオフにしたのにアップロードするのは悪質
コードベース全体をアップロードすると、モデルは「考えている」間、クライアントに実際のツール呼び出しを要求しなくてもコードを見られる
クライアントへ再要求することの欠点が何なのか不明なので、それほど強い理由ではないが、考えうる最善の口実ではある
もともと非公開だったコードが、今では彼らのコードになる
かなり便利ではあるが、Elonにリポジトリ全体を渡すほどの価値はない。拒否できない形にし、しかもまったく公開していなかったという事実は、このデータを預けるべきではないという判断をさらに強める