跳到正文
DEV Community· Gabriel Anhaia·· 1 小時前精選AI 評分79

Grok Build CLI 被要求只回覆「OK」,仍上載完整 Git 歷史記錄

'Reply With OK': Grok Build's CLI Still Uploaded the Whole Git History

AI 導讀

Grok Build CLI 0.2.93 被要求只回覆「OK」,仍把包含 4 次提交、47 個檔案及完整 Git 歷史記錄的儲存庫以 git bundle 上載至 xAI 儲存端點。

推薦理由

文章以 Agent CLI 的用戶端網絡通道為焦點,整理 proxy、canary repo 與 bundle 檢查流程,讓讀者可自行核對 CLI 實際傳出的資料。

正文 · 繁體中文

「只回覆:OK。不要讀取或開啟任何檔案。」

這就是整段提示詞。以 cereblab 名義發佈內容的研究員將它傳送給 Grok Build CLI 0.2.93——xAI 的終端機編碼 Agent,並在中間設置了 mitmproxy。模型作出了回覆。與此同時,CLI 還在背景將該程式碼庫的 git bundle 傳送到 xAI 的儲存端點:4 次提交、47 個檔案,以及完整歷史記錄。從擷取到的 bundle 執行 clone 後,取回了一個早已放入其中、而 Agent 曾被告知不要開啟的檔案,標記字串亦完好無缺(cereblab 的報告)。

第二次測試使用一個約 12 GB、由 Agent 從未讀取過的隨機檔案組成的程式碼庫,量化了兩者之間的差距。傳送至模型的資料約有 192 KB;透過儲存通道傳送的則約有 5.10 GiB,分成 73 個每個約 75 MB 的區塊,每個區塊都收到 HTTP 200 回應,直至擷取在傳輸途中停止(gist、The Hacker News)。

xAI 在 2026 年 7 月 13 日透過伺服器端旗標關閉了上傳功能。值得借鑑的是這種方法:代理伺服器和 canary 程式碼庫,加上一段簡短的 mitmproxy 外掛程式,就能顯示任何 Agent CLI(無論是否 Grok)會傳送甚麼內容。

擷取結果顯示了甚麼

這套設定很簡單。cereblab 在 macOS 上使用消費者版 SuperGrok/X 登入帳戶、信任 mitmproxy 憑證,並將 HTTPS_PROXY 和 SSL_CERT_FILE 指向該代理伺服器。Grok 沒有採用憑證固定,因此所有請求都可讀取(gist)。

同一個執行檔使用兩條通道。模型對話傳送至 cli-chat-proxy.grok.com/v1/responses。儲存流量則傳送至同一主機上的 /v1/storage,另外還有 multipart 初始化請求和 batch_exists 檢查。在一次 3 GB 測試中,cereblab 亦看到資料分塊直接傳送至一個名為 grok-code-session-traces 的 Google Cloud Storage 儲存貯體,不過沒有保留該記錄。Mixpanel 和 grok.com 的事件呼叫亦一併傳送。

關閉私隱設定並沒有改變情況。即使關閉「改善模型」,git bundle 仍成功上傳並收到 200 回應,而伺服器的 /v1/settings 回應仍包含 "trace_upload_enabled": true。cereblab 的解讀是:該設定管的是訓練,而非上傳。

兩條通道都出現了機密資料。在一次 Agent 確實讀取檔案的實際任務中,一個由 git 追蹤、包含虛構 canary 值的 .env,原封不動地進入了模型對話。同一批值亦被放進一個準備傳送至儲存端的 session_state 封存檔(The Hacker News)。

A laptop sends a thin thread to a small speech bubble while a thick burnt-orange pipe carries a whole tree of git branches up into a cloud bucket
一個 CLI,兩條通道:只有幾 KB 傳送至模型,卻有數 GB 傳送至儲存端。

研究員清楚説明瞭研究結果的侷限,你也應如此。擷取結果證明瞭資料曾被傳送、接收和儲存,但不能證明 xAI 曾以任何資料訓練模型。12 GB 測試顯示有數 GB 資料上傳且沒有失敗,但並非完整傳輸;而且只保留了該次測試的記錄。所有測試都使用 0.2.93 版本,而所有「機密資料」都只是虛構的 canary 字串。

7 月 13 日:伺服器切換了旗標

同一個 0.2.93 執行檔在 7 月 13 日停止發出儲存請求。cereblab 重測了 6 次,沒有看到任何 /v1/storage 上傳;伺服器現在則回傳 disable_codebase_upload: true 和 trace_upload_enabled: false(The Hacker News)。

xAI 在 X 回應。據 The Hacker News 報道,xAI 表示,企業級零資料保留方案用戶的程式碼或追蹤資料從未被儲存;一般用戶可以在 CLI 執行 /privacy,關閉資料保留並刪除已同步的資料。Elon Musk 早前表示,已上載的資料將會「完全且徹底刪除」。cereblab 測試了 /privacy,結論是它控制的是資料保留,而非傳送哪些內容(gist)。

7月15日,xAI 發布了 Grok Build 原始碼,其中第一方程式碼採用 Apache 2.0 授權。Tech Times 報道,已發布的原始碼中仍有 Cloud Storage 上載程式碼。Simon Willison 也在當中發現「程式碼的殘餘部分」:upload_session_state() 現在會傳回硬編碼的 session_state_upload_unavailable 錯誤。上載路徑已停用,但相關程式碼仍然存在。

伺服器下發的旗標可由伺服器切回原狀,無需在你的電腦上新增二進制檔,也不會在變更記錄中留下任何資訊。據 The Hacker News 報道,截至當時,xAI 尚未説明這項變更是否涵蓋所有帳戶或是否永久生效、上載資料保留了多久,以及有多少用戶受到影響。

拒絕規則會阻止 Agent,但客戶端仍會上載

cereblab 的重現測試儲存庫提供了最關鍵的細節:像 Read(secret.txt) 這樣的拒絕規則確實阻止 Agent 讀取該檔案,但這個受追蹤的檔案仍包含在上載套件中。

每個 Agent CLI 都有兩層。第一層是模型的工具呼叫:讀取這個檔案、執行那個命令。你的核準提示和拒絕規則都在這一層。另一層是客戶端本身的網絡程式碼,它會執行模型決定的操作。第一層看不到第二層。

cereblab 的比較筆記也清楚列出套件包含的內容:受追蹤的檔案及其歷史紀錄。從未提交過的 Git 忽略檔案不會包含在內;曾經提交、其後刪除的檔案則會一併包含。如果金鑰曾出現在某次提交中,該儲存庫的每個套件都會包含它,因此要輪換金鑰;刪除檔案並不能將它從歷史紀錄中移除。

同一份筆記在7月13日使用同一部機器和一個帳戶,將金絲雀測試儲存庫交由另外三個 CLI 測試。Claude Code 2.1.204 只傳送了 POST api.anthropic.com/v1/messages。Codex 透過 WebSocket 將模型回合傳送至 chatgpt.com,並傳送遙測資料。Gemini 0.38.2 在 API 金鑰模式下,閒置測試沒有顯示任何套件;實際任務測試則遇到 429 配額錯誤。這些測試記錄中,沒有任何一款上載了儲存庫套件。這是同一天測試的四個 CLI,但下週發布的版本可能會有不同表現。

使用代理伺服器和金絲雀測試稽核任何 Agent CLI

你不必盲目信任以上任何內容,包括我所説的。可行情況下,請使用一次性帳戶,正如 cereblab 所建議。

A small canary bird sits inside a cage built from git branch lines, inside an open folder, while a magnifying glass checkpoint watches the path out
放入一些 Agent 絕不能碰的內容,再監察網絡流量,看看它是否出現。

首先,建立一個包含三個金絲雀標記的儲存庫:一個提示詞禁止接觸的檔案、一個只存在於歷史紀錄中的秘密,以及一個從未提交過、被 Git 忽略的秘密。

set -eu
mkdir canary && cd canary && git init -q
git config user.name canary
git config user.email canary@example.com
mkdir -p src/_probe
echo "CANARY-NEVERREAD-7Q2Z9" > src/_probe/never_read.txt
echo "TOKEN=CANARY-HISTORY-4K8M1" > old.env
git add -A && git commit -qm "init"
git rm -q old.env && git commit -qm "remove secret"
echo ".env" > .gitignore
echo "API_KEY=CANARY-IGNORED-9X3V5" > .env
git add .gitignore && git commit -qm "ignore .env"

接著,加入一個外掛程式,按主機統計外傳的位元組數、標記請求本文中的任何金絲雀字串,並儲存任何以 Git bundle 格式開頭的內容。Bundle 以 # v2 git bundle 或 # v3 git bundle 開頭(Git 文件),相關掛鈎則使用標準的 mitmproxy 事件。

# count_egress.py
import collections
import logging

MARKERS = [
    b"CANARY-NEVERREAD-7Q2Z9",
    b"CANARY-HISTORY-4K8M1",
    b"CANARY-IGNORED-9X3V5",
]
BUNDLE_SIGS = [b"# v2 git bundle", b"# v3 git bundle"]
sent = collections.Counter()
saved = 0

def request(flow):
    global saved
    body = flow.request.get_content(strict=False) or b""
    host = flow.request.pretty_host
    path = flow.request.path
    sent[host] += len(body)
    for m in MARKERS:
        if m in body:
            logging.warning("canary %s -> %s%s",
                            m.decode(), host, path)
    for sig in BUNDLE_SIGS:
        at = body.find(sig)
        if at >= 0:
            saved += 1
            name = f"bundle-{saved}.bundle"
            with open(name, "wb") as f:
                f.write(body[at:])
            logging.warning("git bundle -> %s from %s%s",
                            name, host, path)
            break

def done():
    for host, size in sent.most_common():
        logging.warning("%12d bytes  %s", size, host)

使用 mitmdump -p 8080 -s count_egress.py 啟動代理伺服器。首次啟動時,它會將 CA 寫入 ~/.mitmproxy/mitmproxy-ca-cert.pem(mitmproxy 文件)。在另一個終端機中,設定 CLI 經由該代理伺服器連線。Claude Code 會讀取 NODE_EXTRA_CA_CERTS,以載入額外的 CA(Anthropic 文件);SSL_CERT_FILE 是 cereblab 為 Grok 設定的變數。你使用的 CLI 可能有自己的設定,請查閲相關文件。

export HTTPS_PROXY=http://127.0.0.1:8080
CA="$HOME/.mitmproxy/mitmproxy-ca-cert.pem"
export NODE_EXTRA_CA_CERTS="$CA"
export SSL_CERT_FILE="$CA"
cd canary
claude -p "Reply with exactly: OK. Do not read or open any files."
claude -p "What does this project do?"

將 claude 換成你要審核的 CLI。執行兩個提示詞:閒置提示詞會告訴你 Agent 甚麼都不做時有哪些資料傳出;實際任務則會告訴你它執行工作時有哪些資料傳出。按 Ctrl-C 停止 mitmdump,done hook 便會列出各主機的總量。如果有儲存 bundle 檔案,請開啟它:

git clone -q bundle-1.bundle recovered
cat recovered/src/_probe/never_read.txt
git -C recovered log --all --oneline -- old.env

如何解讀結果:

  • 模型主機必須出現在結果中。如果沒有,代表 CLI 忽略了代理伺服器;流量擷取結果沒有動靜,也不能證明任何事。
  • 將各主機與供應商自己的清單比較。例如,Anthropic 會公開列出 Claude Code 會連線的各個主機及其用途。未列於清單上的主機,就需要向供應商查問。
  • NEVERREAD 在閒置測試中出現,表示檔案內容在未被讀取的情況下傳出了。HISTORY 在任何位置出現,都表示歷史記錄正在傳送。
  • 在實際任務測試中,模型主機上的 IGNORED 表示 Agent 讀取了你的 .env。任何其他主機上的 IGNORED,都表示有 git 歷史記錄以外的內容正在傳出。
  • 能成功 clone 的已儲存 bundle,是你能取得的最有力證據。

這套設定有盲點。標記檢查只能檢視未壓縮的本文,而 git 會壓縮 bundle 內的物件,因此對於例如 Grok 的上載,已儲存的 bundle 和各主機的傳輸位元組總數就是證據。單憑沒有出現 canary 警告,不能證明任何事。執行憑證鎖定的 CLI 會拒絕代理伺服器,這本身就是一項發現。request hook 只會計算 HTTP 本文,因此若 CLI 使用 WebSocket 通訊(Codex 在模型回合中便是如此),請加入 websocket_message hook。大型上載可能會分成多個區塊傳送,只有第一個區塊包含 bundle 標頭;而 multipart 本文則需要先移除末尾分界符,git clone 才能接受。CLI 可能會忽略這些環境變數,只讀取 OS 信任存放區;請在一次性用戶帳戶的 OS 信任存放區中信任 CA,之後再將其移除。

Hermes 在這件事中的角色

我開發 Hermes IDE,因此閲讀本節時請將這點納入考量。Hermes 會在真正的終端機中執行各供應商自己的 CLI,並使用該 CLI 自己的登入帳戶,而且不會索取你的 token。CLI 會自行登入並連線,因此它上載的任何內容,都是透過自身連線上載。需要審核的是 CLI。

在 Hermes 中,每項任務都有自己的分支和工作樹,避免各 Agent 覆寫彼此的檔案。工作樹仍共用同一個儲存庫(git 文件),因此從某項任務的工作樹建立的 bundle,可能包含與你的主要工作副本相同的共用歷史記錄。

由於可以加入任何命令列 Agent,這項審核也能融入其中:將代理伺服器的 export 設定放進一個以 exec grok "$@"(或 exec claude "$@")結尾的小型指令碼,再將該指令碼新增為 Agent,並在它自己的分支上交給它 canary 任務。我沒有親自執行 cereblab 的測試框架;上述數字是他們提供的。

Hermes 也會自行發出網絡請求,其版本資訊提到其中兩項。自 2.3.0 起,匿名使用統計預設為開啟,而且絕不包含程式碼、提示詞或檔案名稱;你可以在設定 > 私隱中將其關閉。Hermes 亦會每天最多一次連線至 hermes-ide.com 查詢新聞;關閉「顯示來自 Hermes 團隊的通知」後,這項功能便會停止。這些都是聲稱,而上述設定正是測試這類聲稱的方法。Hermes 免費,採用 BSL 1.1 授權提供原始碼,可在 macOS、Windows 和 Linux 上執行,且與 xAI、Anthropic、OpenAI、Google 或 Nous Research 沒有關聯。


如果本文對你有幫助

本週針對你使用的 Agent 執行金絲雀測試,並保留這個附加元件,供下一個版本使用。若你想在同一處並排執行 Claude Code、Codex 等工具,而且各自使用獨立分支,下載 Hermes IDE;如果覺得有用,也可以在 GitHub 為它加星標。想了解 Agent、其工具及相關防護措施背後的模式,可以閲讀簡短的 AI Agents Pocket Guide。

AI Agents Pocket Guide: Patterns for Building Autonomous Systems with LLMs

來源:DEV Community · dev.to