Passa al contenuto principale

Cosa carica davvero il tuo coding agent

Avanzato
What you'll learn
  • 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:

CanaleEndpointByteRichieste
A — turni del modelloPOST cli-chat-proxy.grok.com/v1/responses196.7055
B — upload del repositoryPOST cli-chat-proxy.grok.com/v1/storage5.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.

Guided walkthrough1 of 6
  1. 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.

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 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 , 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.

Guided walkthrough1 of 6
  1. 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.

Check yourself

0/4
  1. Nella cattura di Grok Build, un file che all'agente era stato esplicitamente detto di non leggere ha comunque lasciato la macchina. Perché?
  2. Il toggle di un vendor "non usare i miei dati per migliorare il modello" è spento. Cosa hai stabilito?
  3. Quale misura rivela in modo più affidabile un Canale B non documentato?
  4. Quale controllo avrebbe impedito questa classe di upload indipendentemente dall'implementazione del vendor?
Premi Invio o Spazio per girare la carta. Usa le frecce sinistra e destra per spostarti tra le carte.Termine mostrato.
1 / 8

Dove 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