プログラミング
xAIのGrok Build CLIが実際にxAIに送信するもの
What xAI's Grok Build CLI Actually Sends to xAI (gist.github.com)
要約
この記事は、xAIのGrok Build CLIツールが、ユーザーのファイル内容(.envファイルなどの機密情報を含む)を未編集のままxAIに送信し、さらにエージェントが読み取ったかどうかにかかわらず、リポジトリ全体とGit履歴をアップロードすることを詳細に分析しています。これらのデータはGoogle Cloud Storageに保存されることが明らかになりました。
全文翻訳
xAIのGrok Build CLIが実際にxAIに送信するもの:ワイヤーレベル分析
測定可能で再現性のある分解。
発見は、キャプチャされたアーティファクト(エンドポイント、HTTPメソッド、ステータスコード、バイトサイズ、ホスト)と再現コマンドによって裏付けられています。観察がライブで行われたがファイルとして保持されなかった場合、§7で明示的に述べられています。
セクション8は、SHA-256と「証明しなかったこと」リストを含む証拠補遺です。
すべてのキャプチャは、使い捨てのリポジトリ(偽の「カナリア」シークレットを含む)を使用して、私の自身のマシン上で私の自身のトラフィックをキャプチャしたものです。実際の認証情報は一切公開されていません。
0. 要約
xAIの公式Grok BuildコーディングCLI(grok)は、通常のコンシューマーログインで、文書化する価値のある3つのことを行います。
それは、読み取ったファイルのコンテンツ(.envシークレットファイルを含む)を、そのまま未編集のままxAIに送信します。
シークレットは2つのチャネルに現れます:ライブモデルターン(POST /v1/responses)と、POST /v1/storage経由でアップロードされ受け入れられた(HTTP 200)セッション状態アーカイブ(バイナリがgrok-code-session-traces GCSバケットにルーティングするエンドポイント(§5参照))。
エージェントが読み取ったものに関係なく、リポジトリ全体(追跡されたすべてのファイルのコンテンツとgit履歴)をアップロードします。
Grokはワークスペースをパッケージ化し、POST /v1/storage経由でアップロードします。
直接証明されたこと:実際のコードベースで、「OKと返信せよ、ファイルを読み取るな」というプロンプトを使用した場合、Grokはリポジトリ全体をgitバンドルとしてアップロードしました(POST /v1/storage → 200)。キャプチャされたバンドルをgit cloneすると、エージェントが開かないように指示されたファイル(src/_probe/never_read_canary.txt)が、そのユニークなマーカーをそのまま、および完全なgit履歴(補遺uploaded_repo.bundle)とともに復元できます。
そして、それはスケールします:12GBの読み取られなかったランダムファイルのリポジトリで、/v1/storageは5.10 GiBを移動しました(すべてHTTP 200、ストリーム途中で切り捨て)。一方、モデルターンチャネルはわずか192 KBしか移動しませんでした。これは、アップロードがコードベースにピン留めされ、読み取られたものにピン留めされていないことを示しています(約27,800倍の比率)。
ストレージアップロードは失敗しませんでした。200以外の唯一のエラーは、/v1/responsesでのモデル使用量クォータ(402/429)と、ストレージサイズの上限ではない無関係な404でした。
ストレージの宛先は、Google Cloud Storageバケット、grok-code-session-traces(AWS S3ではない)です。これは、バイナリ内およびキャプチャされたmetadata.json(gs://grok-code-session-traces/…)でそのままの名前で表示されます。
このメカニズムは、CLIのインストール/クイックスタート資料には表示されませんでした(網羅的なドキュメント監査ではない - §7)。これはデフォルトでアクティブであり、「モデルの改善」を無効にしてもオフになりません(/v1/settingsは依然としてtrace_upload_enabled: trueを返しました;§6)。
これらはどれも、xAIがデータをトレーニングに使用することを証明するものではありません。それは§6で対処されるポリシーの問題です。証明されたのは、送信、受け入れ、およびストレージです。
1. テスト対象(出所)
インストール:curl -fsSL https://x.ai/cli/install.sh | bash # → ~/.grok/bin/grok
認証:最初の起動時にブラウザが開き、X / SuperGrok(コンシューマーアカウント、APIキーではない)にログインします。
バイナリ識別(再現:file $(readlink -f ~/.grok/bin/grok); ~/.grok/bin/grok --version; shasum -a 256 $(readlink -f ~/.grok/bin/grok)):
~/.grok/bin/grok -> ../downloads/grok-macos-aarch64 Mach-O 64-bit実行可能ファイル arm64 grok 0.2.93 (f00f96316d4b)
SHA-256: 2a97ba675bd992aa9b981e2e83776460d94f469b510c0b8efe28b50d236d767c
アップロード機構は、ファーストパーティのRustクレートです。
バイナリのstringsコマンドは、これらのソースパスと定数を生成します(再現:strings <binary> | grep -E 'xai-data-collector|grok-code-session-traces|storage.googleapis'):
crates/codegen/xai-data-collector/src/gcs.rs
crates/codegen/xai-data-collector/src/storage_client.rs
crates/codegen/xai-data-collector/src/queue.rs
crates/codegen/xai-data-collector/src/file_access_tracker.rs
crates/codegen/xai-data-collector/src/circuit_breaker_observer.rs
crates/codegen/xai-grok-shell/src/upload/{gcs,turn,trace,manifest}.rs
grok-code-session-traces
storage.googleapis.com
"Uploading bytes to GCS via proxy"
2. 方法(再現可能)
環境:macOS、Apple Silicon、grok 0.2.93、2026年7月。
brew install mitmproxy; 一度実行してCAを~/.mitmproxy/に生成します。
ログインキーチェーンにCAを信頼します(sudo不要;Grokはこれに対して証明書ピニングを行いません):
security add-trusted-cert -r trustRoot -k ~/Library/Keychains/login.keychain-db ~/.mitmproxy/mitmproxy-ca-cert.pem
プロキシを経由するようにルーティングされたGrokを実行します(mitmdumpアドオンは、リクエストごとにメソッド、ホスト、パス、レスポンスステータス、リクエストバイトサイズをログに記録し、xAIホストのリクエストボディを保存します):
Https_PROXY=http://127.0.0.1:8080 SSL_CERT_FILE=~/.mitmproxy/mitmproxy-ca-cert.pem grok -p "<prompt>" --cwd <repo>
ステージングされたアーティファクトの検査のために、実行中に~/.grok/upload_queue/*をレースコピーし、gzip -dc | tar -xOします。
カナリアリポジトリ:各ファイルにはユニークなマーカーが付いており、キャプチャされたトラフィックに現れるものはすべて、ファイルから明確に追跡できます。
シークレットファイル secrets.env / .env:
API_KEY=CANARY7F3A9-SECRET-should-not-leave
DB_PASSWORD=CANARY7F3A9-DBPASS
3. 発見1 — シークレットファイルを含むファイルコンテンツが送信され、受け入れられる(200)
主張:Grokがファイルを読み取ると、そのコンテンツはxAIに送信されます。これは、POST /v1/responsesモデルターンのボディにシリアライズされ、POST /v1/storage経由でアップロードされ受け入れられる(HTTP 200)セッション状態アーカイブにパッケージ化されます。ファイルのコンテンツの編集はありません。.envは他のファイルと同様に送信されます。
ワイヤーアーティファクト — 復号化された48,070バイトのPOST cli-chat-proxy.grok.com/v1/responsesリクエストボディ(埋め込まれた"messages":[...]"model":"grok-4.5" JSONによってモデルターンとして識別可能)。それはシークレットファイルをそのまま含んでいます(補遺:secrets_responses_body.bin, secret_verbatim.txt):
…API_KEY=CANARY7F3A9-SECRET-should-not-leave\nDB_PASSWORD=CANARY7F3A9-DBPASS\n…"model":"grok-4.5"…
再現:grep -a "CANARY7F3A9-DBPASS" secrets_responses_body.bin → マッチします。6つのファイルマーカーすべて(ソース、ロジック、README、ネストされたJS、APIキー、DBパスワード)は、復号化された/v1/responsesボディから回復可能です。(このアーティファクトは、シークレットが/v1/responsesエンドポイントに送信されたことを証明します。生のボディファイルはレスポンスステータスを保持しないため、受け入れ(200)の主張は、直下の/v1/storageチャネルにアンカーされており、これはwire_12gb.logでステータスマッピングされています。)
2番目のチャネル — Google Cloud Storageに永続化されます。同じコンテンツが、POST /v1/storage経由でアップロードされるセッション状態アーカイブにパッケージ化されます。
ステージングされたアーティファクトがドレインされる前にそれを解凍することによって証明されます(補遺:secrets_session_state.tar.gz):
gzip -dc secrets_session_state.tar.gz | tar -xO | grep -ao 'CANARY7F3A9-[A-Z]*' → CANARY7F3A9-SECRET, CANARY7F3A9-DBPASS, + 他すべて
したがって、シークレットはインフライトで処理されるだけでなく、ストレージ宛のアーカイブに書き込まれます。
「シークレットを読むように指示した」という先走った反論に対して。
エージェントに開かないように指示されたファイル(untouched_secret.txt)と「正確にOKと返信せよ、ファイルを一切読み取るな」というプロンプトを使用した制御実行では、キャプチャされたボディのいずれにもそのファイルのマーカーが出現しませんでした。
したがって、漏洩はGrokが読み取るファイルに限定されます。しかし、それは(タスクに関連するすべてのファイル、.envを含む)広範囲に読み取り、そのファイルのコンテンツを編集しませんでした。
欠陥は、シークレットファイルが編集されずに送信されたことであり、そのファイルの読み取り行為ではありません。
重要なスコープの調整:この制御は、読み取られなかったファイルが/v1/responsesボディに存在しないことを示します。これはチャネルA(エージェントが読み取るファイル)です。これは、§4の別個の全体リポジトリ/v1/storageスナップショット(チャネルB)をクリアしません。これは、そこでのボリューム証拠により、読み取られなかったファイルも取り込む可能性があります。この特定のファイルを確認するために、/v1/storageコードベースチャンクを解凍できませんでした。
したがって、「読み取られなかったファイルはアップロードされない」という主張は、モデルターンチャネルに対してのみ真であり、コードベーススナップショットに対しては真ではありません。
(さらに2つのスコープ注:(i)私の実行では、.env/secrets.envはgitで追跡されていました。gitignoredファイルがまだアップロードされるかどうかは別途テストしていません。そのため、gitignoreに関する主張は行いません。メカニズムはfile_access_trackerクレートごとに読み取り駆動ですが、その特定のケースはテストされていません。(ii)カナリア値は.env/secrets.env内のAPI_KEY=/DB_PASSWORD=キーにありましたが、実際の形式の高エントロピートークンではありませんでした。この.envが編集されずに送信されたことを証明しましたが、例えばsk-…形状のキーに対するエディターが存在しないことを証明したわけではありません。)
4. 発見2 —