Cosa carica davvero il tuo coding agent
- Separare i due canali di egress che ogni coding agent ha — e capire perché solo uno dei due è visibile a te
- Leggere la cattura del traffico di Grok Build: 196 KB di traffico verso il modello contro 5,10 GiB di upload del repository, dalla stessa sessione
- Capire perché un permesso negato e un toggle "non addestrare sui miei dati" possono essere entrambi rispettati mentre il tuo repo esce comunque dalla macchina
- Intercettare il traffico del tuo agente con mitmproxy e un repo canarino, e contare i byte per endpoint con le tue mani
- Confrontare cosa Claude Code, Cursor, Gemini CLI e Codex dichiarano ciascuno di inviare — e dove finisce la documentazione
Quasi certamente modelli la privacy del tuo coding agent così: invia ciò che legge. Approvi la lettura di un file, quel file va al modello. La neghi, non ci va. I tuoi prompt e i file nel contesto sono il carico utile; tutto il resto è metadato.
Quel modello è sbagliato per almeno un agente in produzione, e strutturalmente incompleto per parecchi altri. A luglio 2026 un ricercatore ha puntato un proxy sulla CLI Grok Build di xAI e ha scoperto che un repository da 12 GB usciva dalla macchina come git bundle — cronologia completa, file mai letti, contenuti di .env — attraverso un canale che non aveva niente a che vedere con ciò che l'agente leggeva, mentre l'impostazione che un utente avrebbe cercato per fermarlo governava tutt'altro.
Questa pagina non parla davvero di Grok. Parla del canale che ha esposto, che la maggior parte degli agenti ha in qualche forma, e del fatto che puoi verificare il tuo in un pomeriggio invece di fidarti di una pagina di marketing.
I due canali
Ogni CLI di agente che parla con un modello ospitato ha il Canale A. Alcune hanno anche il Canale B.
Canale A — il turno del modello. Il tuo prompt, il system prompt, le definizioni dei tool, e i contenuti dei file che l'agente ha effettivamente letto. È il canale che la tua intuizione segue. È quello che i prompt di permesso presidiano, quello su cui agiscono le regole di deny in stile .gitignore, quello che puoi grosso modo prevedere dal transcript. Ed è anche piccolo: una sessione di lavoro muove da kilobyte a pochi megabyte.
Canale B — il canale ambientale. Tutto ciò che il client carica di propria iniziativa, non come parte della risposta al tuo turno: indicizzazione del codebase, tracce di sessione, crash report, analytics d'uso, bundle di feedback. Gira secondo la pianificazione del client, non secondo il ragionamento del modello. Non compare nel tuo transcript. Non chiede il permesso. E poiché è guidato da un percorso di codice e non da una decisione dell'LLM, nessuna quantità di prompting lo influenza — dire all'agente "non leggere alcun file" vincola il Canale A e non dice nulla sul Canale B.
Il Canale B non è di per sé sinistro. L'indice lato server di Cursor è un Canale B ed è documentato nel dettaglio. Il pericolo non è la sua esistenza — è che i controlli esposti all'utente sono quasi sempre progettati per il Canale A, così le persone ragionano su un sistema con due uscite guardandone una sola.
La cattura del traffico
Un ricercatore che pubblica come cereblab ha fatto passare Grok Build CLI 0.2.93 (macOS arm64, commit f00f96316d4b) attraverso mitmproxy su 127.0.0.1:8080 con una CA fidata, contro repo usa-e-getta contenenti stringhe canarino piazzate ad arte. L'harness, le prove catturate e i checksum SHA-256 sono pubblicati come repository di riproduzione, quindi questa è un'affermazione che puoi ri-eseguire invece che una in cui devi credere.
Cosa ha mostrato la cattura:
Due endpoint, volumi radicalmente diversi. In una singola sessione contro un repository da 12 GB:
| Canale | Endpoint | Byte | Richieste |
|---|---|---|---|
| A — turni del modello | POST cli-chat-proxy.grok.com/v1/responses | 196.705 | 5 |
| B — upload del repository | POST cli-chat-proxy.grok.com/v1/storage | 5.476.228.005 (5,10 GiB) | 82, tutte HTTP 200 |
Un rapporto di circa 27.800×. Quel divario è tutto l'argomento: un volume del genere non può provenire da ciò che il modello ha letto. Proviene dal codebase stesso. Gli upload uscivano in chunk da ~75 MB (73 di essi nella sessione registrata) e la cattura è stata interrotta mentre stavano ancora andando a buon fine, il che significa che 5,10 GiB è un pavimento, non un totale.
Il canarino mai letto. All'agente è stato chiesto, in termini espliciti, Reply exactly OK, do not read any files. Un file in src/_probe/never_read_canary.txt contenente un marcatore unico non è comparso in nessun body di /v1/responses — il Canale A si è comportato esattamente come istruito. È invece comparso in un git bundle caricato, recuperato dalla richiesta /v1/storage intercettata. Il ricercatore ha fatto git clone del bundle catturato e ha riottenuto il repository, con marcatore e cronologia completa intatti. Un secondo canarino, una finta password di DB in un file .env, è comparso alla lettera e non oscurato in entrambi i canali.
La destinazione era un bucket con un nome. Stringhe nel binario e metadati di upload preparati puntavano a un bucket Google Cloud Storage, grok-code-session-traces, con path degli oggetti della forma gs://grok-code-session-traces/repo_changes_dedup/v2/…. Nulla nei materiali di setup della CLI descriveva tutto questo.
La correzione, e la forma della risposta. Il 2026-07-13 lo stesso identico binario 0.2.93 ha smesso di emettere richieste /v1/storage in sei nuovi test — un flag flip lato server (disable_codebase_upload: true), non un aggiornamento del client. xAI ha risposto su X invece che con un avviso formale: i clienti enterprise con zero data retention sono stati dati come non impattati, gli abbonati individuali sono stati indirizzati a un comando /privacy, e i dati caricati in precedenza sono stati dichiarati cancellati. Considera lo stato attuale come risolto-per-ora; considera la classe di bug come permanente.
Tre cose che questo demolisce e a cui probabilmente credevi
★ "L'agente invia solo ciò che legge." Falso ogni volta che esiste un Canale B. Leggere è una decisione dell'LLM; indicizzare è un percorso di codice. Non sono lo stesso sottosistema e non condividono lo stesso gate.
★ "Ho negato il file, quindi è al sicuro." Un permesso negato opera al livello dei tool — impedisce all'agente di tirare il file nel contesto. Non è una regola di egress di rete. Nel caso Grok, un file negato è comunque partito dentro il git bundle. Se il tuo threat model richiede che un file non lasci la macchina, il punto di applicazione dev'essere il filesystem o la rete, non il prompt di permesso dell'agente.
★ "Non addestrare sui miei dati" ≠ "non trasmettere i miei dati". Sono due controlli diversi e i vendor espongono abitualmente solo il primo. Il toggle "Improve the model" di Grok mappava sul consenso all'addestramento; con quello spento, il server restituiva comunque trace_upload_enabled: true e gli upload del repo proseguivano invariati. La documentazione di Anthropic traccia esplicitamente la stessa distinzione per Claude Code — l'impostazione sui dati di addestramento e gli interruttori di telemetria/feedback sono manopole separate con variabili d'ambiente separate. Leggi ogni toggle sulla privacy come una risposta a "puoi tenerlo?", mai come una risposta a "lo prenderai?"
Intercetta il traffico del tuo agente
Non devi fidarti della documentazione di nessuno — inclusa questa pagina. Quindici minuti e un proxy ti danno la verità di fatto per qualunque agente tu usi. Fallo su un repo usa-e-getta con segreti finti, sulla tua macchina.
- mitmproxy genera una CA locale al primo avvio. Installala nel trust store di sistema così le chiamate TLS dell'agente terminano al proxy invece di fallire. È tutto qui il trucco — le CLI degli agenti sono comuni client HTTPS e la maggior parte rispetta le variabili d'ambiente standard del proxy.
- Crea un repo git usa-e-getta. Piazza un marcatore unico in un file .env (una credenziale finta), e un secondo marcatore unico in un file che dirai esplicitamente all'agente di non aprire mai. Committa entrambi — i file committati sono ciò che un upload di git bundle trasporterebbe. I marcatori devono essere stringhe che non possono comparire per caso, così una grep sulla cattura è inequivocabile.
- Punta HTTPS_PROXY sul listener e dai all'agente un task che non richiede di leggere assolutamente nulla. Se l'agente usa solo il Canale A, una sessione quasi vuota dovrebbe muovere quasi zero byte.
- Somma le dimensioni dei body delle richieste raggruppate per host e path. È questa la misura che conta. Una sessione che non legge nulla ma muove centinaia di megabyte ha un Canale B, qualunque cosa dica la documentazione.
- Cerca nei flussi grezzi — e decomprimi eventuali archivi o bundle che trovi — entrambi i marcatori. Il marcatore mai-letto che compare da qualche parte nella cattura è un risultato positivo: qualcosa sta caricando file indipendentemente da ciò che il modello ha letto.
- Ora spegni consenso all'addestramento, telemetria, analytics, indicizzazione — tutto ciò che il vendor espone — e ripeti. Il delta tra le due sessioni ti dice esattamente cosa controllano davvero quei toggle, che è la domanda a cui l'etichetta del toggle raramente risponde.
Harness minimo per l'intercettazione
# 1. proxy + CA (macOS; on Linux use your distro's trust store)
brew install mitmproxy
mitmdump -w flows.mitm --set confdir=~/.mitmproxy &
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain ~/.mitmproxy/mitmproxy-ca-cert.pem
# 2. canary repo
mkdir -p /tmp/canary/src/_probe && cd /tmp/canary && git init
echo 'DB_PASS=CANARY-AAAA-ENVSECRET' > .env
echo 'CANARY-BBBB-NEVERREAD' > src/_probe/never_read.txt
git add -A && git commit -m "canary"
# 3. run the agent through the proxy, asking it to read nothing
HTTPS_PROXY=http://127.0.0.1:8080 HTTP_PROXY=http://127.0.0.1:8080 \
<your-agent-cli> "Reply exactly OK. Do not read any files."
# 4. bytes per endpoint — the number that matters
mitmdump -nr flows.mitm \
--set console_eventlog_verbosity=info \
-s <(echo 'def response(f): print(f.request.pretty_host, f.request.path,
len(f.request.raw_content or b""))')
# 5. did a file you never opened leave the machine?
strings flows.mitm | grep -c 'CANARY-BBBB-NEVERREAD'Se il passo 5 restituisce qualcosa di diverso da 0, hai trovato un Canale B — e l'hai trovato tu, che è l'unico tipo di scoperta che invecchia bene.
Cosa dichiarano le CLI principali
La documentazione è un'affermazione; una cattura del traffico è una prova. Il divario tra le due è l'intera lezione di questa pagina. Detto questo, le affermazioni differiscono abbastanza da valere la pena di conoscerle — e conoscerle ti dice cosa cercare quando fai la cattura.
Claude Code. Nessuna indicizzazione dell'intero repo né canale di upload a bundle compare nel flusso dati documentato; l'agente cerca con i tool e invia i contenuti dei file come parte del turno del modello, quindi i contenuti dei file che escono sono quelli tirati nel contesto. La telemetria operativa è separata e, secondo la documentazione di Anthropic, "non include alcun codice o percorso di file" (DISABLE_TELEMETRY=1); il reporting degli errori via Sentry è di nuovo separato (DISABLE_ERROR_REPORTING=1). I canali che sì trasportano codice sono espliciti e opt-in: /feedback invia la cronologia della conversazione incluso il codice (DISABLE_FEEDBACK_COMMAND=1), e il passo di condivisione del transcript nel sondaggio sulla qualità della sessione carica un transcript solo se scegli attivamente Sì, con i pattern noti di chiavi e token oscurati prima. CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC è l'unico interruttore generale per tutto il gruppo. Conservazione: 30 giorni per il commerciale (Team/Enterprise/API), 30 giorni per i consumer che lasciano spento il miglioramento del modello e 5 anni per chi lo accende; la zero data retention esiste per gli account Enterprise che ne hanno i requisiti. Anthropic non addestra sul codice inviato sotto termini commerciali a meno che l'organizzazione non scelga di aderire.
Cursor. Un Canale B documentato da manuale: l'indice del codebase viene costruito spezzando il repo in chunk, calcolandone gli embedding, e inviando gli embedding più i percorsi dei file cifrati ai server di Cursor. Secondo la documentazione, il contenuto del codice è tenuto in memoria durante l'indicizzazione e poi scartato invece che archiviato in chiaro, e i chunk vengono decifrati lato client quando l'agente li recupera. Questo è un vero upload dell'intero repo di un artefatto derivato — un profilo di rischio materialmente diverso da un git bundle grezzo, e materialmente diverso dal modello leggi-solo-ciò-che-ti-serve di Claude Code. Quale dei tre vuoi è una questione di giudizio. Non sapere quale stai usando non lo è.
Gemini CLI. Le statistiche d'uso sono attive di default e si disabilitano con privacy.usageStatisticsEnabled: false in settings.json; la documentazione afferma che il contenuto di prompt e risposte e i contenuti dei file letti o scritti non vengono registrati. La strumentazione OpenTelemetry è separata e può essere puntata su un collector che controlli tu (telemetry.enabled: false per spegnerla).
Codex CLI. Gli analytics anonimi del client sono attivi di default e si disabilitano tramite il flag di configurazione degli analytics; l'export OTel è instradabile sul tuo collector, e il logging dei prompt (log_user_prompt) è spento a meno che tu non lo abiliti. I transcript di sessione persistono in locale sotto CODEX_HOME — vedi sotto.
La metà che nessuno controlla: il tuo disco
L'egress è solo una direzione. Ognuno di questi agenti scrive anche la tua sessione — prompt, contenuti dei file, output dei tool, a volte segreti — sul disco locale in chiaro, e quei file sopravvivono alla sessione.
Claude Code archivia i transcript sotto ~/.claude/projects/ per 30 giorni di default (regolabile con cleanupPeriodDays). Codex conserva la cronologia sotto CODEX_HOME. Grok Build preparava i suoi upload in ~/.grok/upload_queue — a quanto riportato nell'ordine dei gigabyte per turno, e sotto carico capace di crescere fino a riempire il disco, il che è un bug di denial-of-service nascosto dentro un bug di privacy.
Conseguenza: un backup del portatile, una cartella sincronizzata, o un secondo processo con accesso in lettura alla tua home directory sono un percorso di esfiltrazione che non tocca mai lo stack di rete che hai appena strumentato. Qualsiasi segreto che sia entrato in una sessione ora è su disco in chiaro in un posto in cui non l'hai messo tu.
Un hardening che regge davvero
Il tema ricorrente nella discussione su Hacker News — centinaia di commenti nei due thread — era che le istruzioni a livello di markdown non sono un confine di sicurezza. CLAUDE.md e i suoi equivalenti sono indicazioni per un modello, non applicazione contro un binario. Se un controllo deve reggere contro il client, deve stare sotto il client.
- Fallo girare in un container o devcontainer con montato solo il repo di lavoro. È il cambiamento a più alta leva: un agente che non può vedere ~/.ssh, ~/.aws, la cronologia della tua shell, i repo dei tuoi altri clienti o il tuo password vault non può caricarli attraverso nessun canale, documentato o meno. Un utente di sistema separato ottiene buona parte dello stesso risultato con meno cerimonie.
- Nega in uscita di default dal container dell'agente e consenti solo l'host dell'API del modello di cui ha bisogno. Un Canale B che non hai mai approvato non può chiamare fuori attraverso una policy di egress chiusa. Come bonus, questo converte ogni futuro canale di upload non documentato da incidente a errore di connessione nei tuoi log.
- Il .env nel tuo working tree è il singolo file più prezioso che un agente possa far trapelare, ed è quello che più probabilmente è committato e quindi trasportato da qualsiasi upload a forma di bundle. Inietta i segreti da un secret manager o dall'ambiente a runtime; tienili fuori dal repo e fuori dall'albero montato.
- Esegui la cattura della sezione precedente una volta, con tutte le impostazioni di privacy al massimo. Quindici minuti ti comprano un fatto invece di un'affermazione — ed è l'unico passo qui che avrebbe intercettato questo incidente prima del ricercatore.
- Non provare a ricostruire cosa sia stato caricato e cosa no. Se chiavi SSH, credenziali cloud o token API erano raggiungibili in una directory a cui un agente aveva accesso durante una finestra di esposizione, trattali come divulgati e ruotali. La rotazione delle credenziali costa poco; una discussione forense su quali byte si siano mossi no.
- Zero data retention e termini commerciali non sostituiscono i controlli qui sopra, ma cambiano cosa a un vendor è permesso conservare e che ricorso hai. Se stai facendo girare un agente su un codebase che non ti puoi permettere di far trapelare, il tier consumer è il tier sbagliato.
Check yourself
0/4Dove va a finire tutto questo
Niente in questo incidente era specifico del modello, e niente richiedeva un attacco inedito. Richiedeva un client che facesse qualcosa dal suono ragionevole — mettere in cache il repo lato server per dare all'agente un contesto migliore — senza dichiararlo, e una schermata di impostazioni il cui vocabolario ("Improve the model") non mappava sulla domanda che gli utenti si stavano davvero ponendo.
Entrambe le cose sono estremamente comuni. La categoria delle CLI per agenti ha diciotto mesi, si muove in fretta, e rilascia funzionalità che caricano codice perché caricare codice rende il prodotto migliore. Aspettatene altri di casi così. La posizione difendibile non è scegliere il vendor con la migliore pagina sulla privacy; è costruirsi l'abitudine di misurare, e far girare la cosa dentro una scatola che limiti quanto può costare una risposta sbagliata.
Se vuoi il lato avversariale dello stesso problema — contenuto non fidato che pilota un agente che ha già i tuoi permessi — leggi Quando i coding agent vengono usati come armi e Mettere in sicurezza le esecuzioni autonome. Per come le CLI differiscono su tutto ciò che non è il flusso dei dati, vedi CLI per Coding Agent a confronto.
Fonti e approfondimenti
- cereblab — "What xAI Grok Build CLI actually sends to xAI: a wire-level analysis (grok 0.2.93)" — la cattura primaria: endpoint, conteggi di byte, metodologia dei canarini, hash dei binari.
- cereblab/grok-build-exfil-repro — l'harness mitmproxy, il repo canarino e la directory delle prove. Riproducilo tu stesso.
- Hacker News — "Grok CLI uploaded the whole home directory to GCS" — 400+ commenti, per lo più sulle strategie di isolamento.
- Hacker News — "xAI's Grok Build CLI Uploads Git Repositories to a Google Cloud Bucket" — il thread sull'analisi a livello di traffico.
- The Hacker News — "Grok Build Uploaded Entire Git Repositories to xAI Storage" — cronologia e risposta di xAI.
- Anthropic — Claude Code data usage — telemetria, feedback, conservazione, e le variabili d'ambiente che controllano ciascuna cosa.
- Cursor — Codebase indexing — cosa carica l'indice e cosa non archivia.
- Gemini CLI — configurazione e telemetria —
usageStatisticsEnabled, instradamento OTel. - mitmproxy — lo strumento che trasforma un'affermazione sulla privacy in una misura.