Je coding agent las je .env en stuurde je hele repo weg
7m leestijd

Je coding agent las je .env en stuurde je hele repo weg

Een analyse van het netwerkverkeer betrapte de Grok Build CLI op het wegsturen van een complete repository, git-historie en een onafgeschermde .env naar een bucket van de leverancier. Zo controleer je wat je eigen coding agent verstuurt, en welke secrets nooit op een agent-machine horen.

Op 13 juli deed iemand het enige wat vrijwel niemand ooit doet. Hij zette zijn coding agent achter een proxy en keek wat het ding werkelijk verstuurde.

Het ging om Grok Build CLI van xAI, versie 0.2.93. De onderzoeker, bekend als cereblab, draaide de tool op een repository van 12 GB en ving het verkeer op met mitmproxy. Er kwamen twee getallen uit.

Het model had zo'n 192 KB nodig. De client uploadde 5,1 GB.

Dat tweede getal ging niet naar het model. Het ging naar een Google Cloud-bucket met de naam grok-code-session-traces, in 73 stukken van ongeveer 75 MB, elke request netjes een HTTP 200. Een verschil van ongeveer 27.800 keer tussen wat het model las en wat de machine verliet.

En toen kwam het deel waar je maag van omdraait. Een .env-bestand dat in de repo was geplant, met daarin API_KEY=CANARY7F3A9-SECRET-should-not-leave, kwam letterlijk terug in de capture. Onafgeschermd. Zowel in de request naar het model als in een session_state.tar.gz die naar de bucket werd geüpload.

De secret die er nooit uit had mogen gaan, ging eruit.

Wat er precies gebeurde

Dit was niet de agent die een bestand las waar je hem naartoe wees. De capture liet twee losse kanalen zien.

Het eerlijke kanaal: POST /v1/responses, met daarin de bestanden die Grok las tijdens het werk. En het andere: POST /v1/storage, dat een kopie van de complete codebase uploadde, ongeacht wat het model aanraakte.

cereblab bewees die scheiding met een gemene test. De prompt was "Reply exactly OK, do not read any files." Grok antwoordde OK. En uploadde ondertussen een uploaded_repo.bundle van 152 KB. Een git clone op die opgevangen bundle bouwde de hele repository terug: 47 bestanden, 4 commits aan historie, inclusief een bestand never_read_canary.txt dat de agent nooit had geopend. Voor de zekerheid nog een keer herhaald op een tweede, losstaande codebase.

Dus de hele repo, bestanden die het model nooit zag, plus volledige git-historie, plus je secrets, ging de deur uit. En dan het detail dat een bug in iets ergers verandert: de instelling die op je uitknop leek, stuurde iets heel anders aan.

In de interface stond een schakelaar "Improve the model". Zet die uit en je gaat er redelijkerwijs van uit dat je code niet meer weggaat. Maar nee. Die schakelaar ging alleen over toestemming voor training. De /v1/settings-endpoint gaf nog steeds trace_upload_enabled: true terug nadat je hem had uitgezet. De knop die de upload wél stopte, disable_codebase_upload, zat in een configbestand waarvan de meeste gebruikers niet wisten dat het bestond.

Je had een schakelaar met het label privacy. Hij was nergens op aangesloten.

Waarom je rustig kunt blijven

Dan het deel dat dit eerlijk houdt, want de boodschap is niet dat één bedrijf uniek kwaadaardig is.

cereblab draaide dezelfde canary-tests op de andere tools. Claude Code en Codex stuurden geen repository-bundle. De CLI van Gemini stuurde niets in ruststand. Grok Build was hier de uitzondering. Werk je met Claude Code, dan vertoont je client dit gedrag vandaag niet.

Zakelijke klanten met zero data retention waren, volgens xAI, nooit geraakt. En zodra de gist op de voorpagina van Hacker News belandde, stopten de storage-uploads binnen een dag, uitgezet via een server-side vlag, en postte Musk dat de verzamelde data "completely and utterly deleted" zou worden.

Dus: één leverancier, één client, grotendeels opgelost, concurrenten schoon. Je zou dat allemaal kunnen lezen en besluiten dat er niets aan de hand is.

Ik lees het andersom.

Het deel dat je wél zorgen zou moeten baren

De fix kwam een dag nadat een willekeurige buitenstaander toevallig het verkeer opving en het toevallig op het juiste forum zette. Niet omdat een controle luid faalde. Niet omdat een log opeens rood kleurde. Omdat iemand keek.

Elke upload gaf een HTTP 200. Vanuit de tool gezien was er niets aan de hand. De enige reden dat wij dit weten, is dat één persoon zijn client genoeg wantrouwde om hem achter mitmproxy te zetten en de bytes te lezen. Vertrouwen was het enige wat tussen je repo en die bucket stond, en vertrouwen is geen beveiliging. Het is juist het ontbreken ervan.

Er zit ook een stillere les in de techniek. De upload-code verdween niet. Hij bleef in de binary, ook in latere builds, alleen tegengehouden door een server-side vlag die de leverancier beheert. Wat vanaf een server is uitgezet, kan vanaf een server weer worden aangezet, zonder dat jij ook maar één update binnenkrijgt. Je vertrouwt op een belang van de leverancier. Dat is iets anders dan een garantie. En het belang om echte codebases binnen te halen, precies de trainingsdata waar iedereen naar snakt, wijst de verkeerde kant op. Dit is dezelfde zwaartekracht waar ik over schreef in de teller stond altijd al te lopen: de gratis, wrijvingsloze tool heeft een verdienmodel, en uiteindelijk komt dat verdienmodel bij jou uit.

De oude wereld had een natuurlijke rem. Als je code in een webchat plakte, besloot een mens, bewust, wat erin ging. Die wrijving was lelijk en traag en beschermde je ook. Geef een CLI de sleutels van je werkmap en die wrijving is weg. De agent bepaalt wat er vertrekt, en jij komt er later achter, als je er al achter komt.

Wat je concreet moet doen

Je kunt een belofte niet controleren. Een verbinding wel. Hier de praktische versie.

  • Bekijk je eigen CLI, één keer. Stuur hem door een proxy en lees wat hij verzendt. Het recept van cereblab is saai en herhaalbaar: draai mitmproxy, vertrouw zijn CA lokaal, en start de tool met HTTPS_PROXY=http://127.0.0.1:8080 en SSL_CERT_FILE naar het proxy-certificaat. Doe een triviale taak. Kijk naar de endpoints. Zie je megabytes stromen terwijl de taak kilobytes nodig had, dan heb je je antwoord. Doe dit één keer per tool die je vertrouwt en je gokt nooit meer.
  • Houd secrets volledig buiten de agent-machine. De schoonste verdediging tegen een .env-lek is dat er geen echte .env is om te lezen. Echte sleutels horen in een secrets manager of de keyring van je shell, tijdens runtime geïnjecteerd, nooit weggeschreven in de werkmap die de agent kan zien. Een .env vol productiesleutels die eruitziet alsof hij is ingecheckt, is een geladen wapen op tafel. Dit is hetzelfde principe achter een agent afgeschermde, intrekbare toegang geven in plaats van de echte credentials.
  • Ga ervan uit dat de client onbetrouwbaar is, net als elke andere dependency. Je controleert een MCP-server ook voordat je hem installeert. Een closed-source CLI met netwerktoegang en leesrechten op je hele bestandsboom verdient net zo goed die argwaan. Kijk wat hij uploadt, wat zijn privacy-schakelaar werkelijk aanstuurt, en of "uit" ook uit betekent op de server of alleen in de interface.
  • Roteer alles wat een agent heeft gezien. Heeft een coding agent een sleutel gelezen, behandel hem dan als mogelijk gelogd, in een transcript beland of ergens geüpload waar jij niet bij kunt. Nu roteren is goedkoop, het later uitleggen in een postmortem is duur.

Niets hiervan vraagt je om coding agents af te zweren. Ik gebruik ze elke dag. Het vraagt je om te stoppen met de client als neutraal leidingwerk te zien, en hem te behandelen voor wat hij is: software met zijn eigen belangen, draaiend met jouw rechten, op een machine vol jouw secrets.

De repo verliet de machine en de developer klikte nergens op. Dat is het hele verhaal, en het is alleen een verhaal omdat één iemand naar de verbinding keek.

Kijk nu eens naar die van jezelf.