Os dados que o xAI Grok Build CLI envia para a xAI: análise no nível do wire
(gist.github.com/cereblab)- Ao capturar diretamente o tráfego de rede do
grok 0.2.93, foi constatado que o Grok Build enviava os arquivos lidos sem mascaramento e os armazenava emsession_state, incluindo também valores secretos de teste em.envintegralmente em dois caminhos - Separadamente das requisições ao modelo que enviam os arquivos lidos pelo agente, todo o repositório com todos os arquivos rastreados e o histórico Git foi enviado como um git bundle, e até arquivos marcados para não serem abertos puderam ser recuperados integralmente
- Em um repositório com 12GB de arquivos aleatórios, as requisições para
/v1/responsestotalizaram 192KB, mas o volume enviado para/v1/storagechegou a 5.10GiB até o momento em que a captura foi interrompida, uma diferença de cerca de 27.800 vezes, e todas as requisições de armazenamento retornaram HTTP 200 - O destino dos uploads era o bucket
grok-code-session-tracesno Google Cloud Storage, e mesmo com “Improve the model” desativado,trace_upload_enabled: trueeupload_enabled: truecontinuaram ativos, com o upload do repositório inteiro prosseguindo - O experimento comprova a transmissão, aceitação e armazenamento dos dados, mas não confirmou se eles foram usados no treinamento do modelo; além disso, não testou arquivos
.gitignorenem todas as combinações de conta e configuração, então os resultados se limitam a uma versão específica de julho de 2026
Objeto de teste e escopo da análise
- O alvo foi o Grok Build CLI oficial da xAI, autenticado com uma conta comum de consumidor
- O caminho de instalação era
~/.grok/bin/grok - A autenticação era feita no navegador com conta X ou SuperGrok, sem uso de chave de API
- O binário testado era
grok 0.2.93 (f00f96316d4b)para Apple Silicon - O SHA-256 era
2a97ba675bd992aa9b981e2e83776460d94f469b510c0b8efe28b50d236d767c
- O caminho de instalação era
- Nas strings do binário, foram encontrados componentes próprios de upload em Rust e constantes relacionadas a armazenamento
crates/codegen/xai-data-collector/src/gcs.rsstorage_client.rs,queue.rs,file_access_tracker.rs,circuit_breaker_observer.rsxai-grok-shell/src/upload/{gcs,turn,trace,manifest}.rsgrok-code-session-traces,storage.googleapis.com,Uploading bytes to GCS via proxy
- Todas as capturas foram limitadas ao computador e ao tráfego do próprio pesquisador, e no repositório foram usadas strings canary exclusivas no lugar de credenciais reais
Como reproduzir e capturar o tráfego
- Em um macOS com Apple Silicon, foi adicionada ao chaveiro de login a CA do
mitmproxycomo certificado confiável, eHTTPS_PROXYeSSL_CERT_FILEforam configurados para capturar as requisições HTTPS do Grok - Nessa configuração, o Grok não bloqueou a captura com certificate pinning
- Um addon do
mitmdumpregistrou método, host, caminho, status de resposta e tamanho em bytes de cada requisição, além de salvar os corpos das requisições enviadas aos hosts da xAI - Os arquivos em
~/.grok/upload_queue/*foram copiados durante a execução e depois inspecionados após extração comgzipetar, permitindo examinar também os artefatos de staging aguardando envio - Cada arquivo recebeu um marcador exclusivo para identificar sua origem, e os arquivos secretos usaram valores falsos como os seguintes
API_KEY=CANARY7F3A9-SECRET-should-not-leaveDB_PASSWORD=CANARY7F3A9-DBPASS
Canal A: envio ao modelo dos arquivos lidos e do .env
- O conteúdo dos arquivos lidos pelo Grok foi serializado no corpo do turno do modelo em
POST cli-chat-proxy.grok.com/v1/responses- A requisição capturada de 48.070 bytes incluía
"model":"grok-4.5"e um array de mensagens - Os canaries
API_KEYeDB_PASSWORDdo.envapareceram integralmente, incluindo as quebras de linha - Foi possível recuperar no corpo da requisição 6 marcadores, incluindo código-fonte, lógica, README, arquivo JavaScript aninhado e os dois valores secretos
- A requisição capturada de 48.070 bytes incluía
- O mesmo conteúdo também entrou no arquivo
session_statee foi enviado emPOST /v1/storage, e essa requisição de armazenamento foi aceita com HTTP 200- Ao extrair e inspecionar o arquivo compactado, os dois valores secretos e os demais marcadores de arquivo também foram encontrados
- Isso mostra que os valores secretos não foram apenas processados temporariamente pelo modelo, mas também registrados em um arquivo para armazenamento
- Em um prompt de controle com
Reply exactly OK, do not read any files, o marcador deuntouched_secret.txt, que não foi aberto, não apareceu no corpo de/v1/responses- Isso comprova apenas que o canal de requisição ao modelo é centrado nos arquivos efetivamente lidos pelo agente
- Como um canal separado de snapshot do repositório inteiro inclui até arquivos não lidos, esse controle não invalida o upload do repositório completo
- Os valores secretos estavam em
.envousecrets.enve eram arquivos rastreados pelo Git- Não foi testado separadamente se arquivos incluídos em
.gitignoretambém são enviados - Como os canaries não imitavam tokens reais de alta entropia, isso não prova que não exista algum mascaramento específico para formatos como
sk-… - O que foi confirmado é que o conteúdo de
.envtestado foi transmitido sem mascaramento
- Não foi testado separadamente se arquivos incluídos em
Canal B: upload do repositório inteiro, incluindo arquivos não lidos
- O endpoint
/v1/storagefazia upload de um snapshot completo do workspace, separado de/v1/responses, que envia os arquivos lidos pelo modelo - Isso foi executado em um repositório real
cereblab_apiapós a instruçãoReply with exactly: OK. Do not read or open any files.- O Grok enviou um git bundle de 152.102 bytes para
POST /v1/storage, e o servidor respondeu com HTTP 200 - Ao executar
git clonenouploaded_repo.bundlecapturado, foi possível recuperarsrc/_probe/never_read_canary.txt, arquivo marcado para não ser aberto - O marcador
CANARY-XR47P2-NEVERREAD-UNIQUEdentro do arquivo permaneceu integralmente intacto - O bundle incluía 4 commits, 47 arquivos e todo o histórico Git
- O Grok enviou um git bundle de 152.102 bytes para
- O mesmo resultado foi reproduzido em um segundo repositório independente,
cereblab_auth- Um git bundle de 31.743 bytes foi enviado por
POST /v1/storagee retornou HTTP 200 - No repositório restaurado, o marcador
CANARY-AUTH-4T8K2-NEVERREADde um arquivo não lido foi confirmado integralmente
- Um git bundle de 31.743 bytes foi enviado por
- Em uma execução separada do
cereblab_apicom 298 arquivos, o manifest em staging listava inclusive arquivos não lidossrc/_probe/never_read_canary.txtfoi registrado junto com código real comosrc/apns.tsesrc/index.ts- O
fileIdpor arquivo emmetadata.jsontinha o formatogs://grok-code-session-traces/repo_changes_dedup/v2/supplemental/sha256_… - Essa execução não concluiu o envio dos blobs de conteúdo por causa de limite de cota, mas os experimentos separados com git bundle e com 12GB confirmaram o upload real
- O binário também continha strings como
after_codebase.tar.gz,repo_state.upload,collecting workspace filesespawning background coordinator, alinhadas com um mecanismo de coleta do workspace
Transferência de vários GiB confirmada em repositório de 12GB
- O comportamento de upload foi observado ampliando o repositório com arquivos aleatórios quase incompressíveis
- Em 64MB, um único
POST /v1/storagede cerca de 48MB retornou HTTP 200, mas o log não foi preservado - Em cerca de 600MB, várias dezenas de chunks de cerca de 7,5MB também retornaram HTTP 200, mas os logs não permaneceram
- Em cerca de 3GB, após inicialização multipart, foram observados PUTs de 50MB para
storage.googleapis.com, mas os logs diretos desses PUTs não foram preservados - Em cerca de 12GB, chunks de cerca de 75MB continuaram sendo enviados para
/v1/storage, e a captura foi interrompida em 5.10GiB
- Em 64MB, um único
- O
wire_12gb.logpreservado contém 83 respostas HTTP 200 para/v1/storage*- 82
POST /v1/storagede upload de conteúdo - Desses, 73 eram chunks de cerca de 75MB
- A soma dos 73 chunks foi
5,476,083,317B, ou 5.10GiB / 5.48GB - Também houve 9 POSTs pequenos e 1 verificação de duplicidade em
/v1/storage/batch_exists - O total de bytes requisitados para
/v1/storage*foi5,476,228,005B, sem nenhuma falha de armazenamento
- 82
- Como a captura foi interrompida enquanto o upload ainda prosseguia, isso não prova a conclusão do envio dos 12GB completos
- O último registro também foi um HTTP 200 para um chunk de cerca de 75MB
- O que fica comprovado é que mais de 5.10GiB foram enviados sem erro e o volume seguia crescendo no momento da interrupção
- Houve grande diferença de volume entre os dois canais na mesma sessão
/v1/responsessomou196,705Bem 5 requisições, cerca de 192KB, e a maior delas tinha 60.394B/v1/storagetotalizou 5.10GiB- A diferença foi de cerca de 27.800 vezes, mostrando que um grande volume de dados do repositório, ausente das requisições ao modelo, foi transferido por um canal de armazenamento separado
- Erros de cota do modelo e upload de armazenamento ocorreram de forma independente
- Em
/v1/responses, houve 1 HTTP 402 e 3 HTTP 429 - Em uma requisição de bookkeeping da sessão, ocorreu 1 HTTP 404 sem relação
- Mesmo após o primeiro 429, seguiram-se 76 respostas HTTP 200 para
/v1/storage - Todas as 82 requisições de armazenamento foram bem-sucedidas e, no escopo do teste, não houve erro de limite de armazenamento
- Em
- Esse resultado não depende do fato de a
upload_queuelocal esvaziar- A fila pode ser esvaziada tanto por upload bem-sucedido quanto por descarte, então ela não foi usada como evidência
- A base probatória foram as capturas mostrando os corpos realmente enviados pela rede com HTTP 200 e a recuperação de arquivos a partir dos git bundles enviados
Local de armazenamento e telemetria
- O destino do armazenamento foi identificado não como AWS S3, mas como o bucket
grok-code-session-tracesno Google Cloud Storage- O binário continha
grok-code-session-traces,storage.googleapis.comeUploading bytes to GCS via proxy - O
metadata.jsonpreservado registrava o destino dos arquivos comogs://grok-code-session-traces/… - No experimento de cerca de 3GB, também foram observados PUTs multipart diretos para esse host do GCS, embora os logs não tenham sido preservados
- Apesar de o binário incluir
aws-sdk-s3, o destino confirmado nos testes foi o GCS
- O binário continha
- Também foram observadas requisições de telemetria para terceiros e para a própria empresa
api.mixpanel.com/tracke/engageda Mixpanelgrok.com/_data/v1/events- Todas essas requisições retornaram HTTP 200
- Nos scripts de instalação do CLI e no material de quickstart revisados, não foram encontradas referências a uploads de
repo_state,session_state,~/.grok/upload_queueougrok-code-session-traces- Como nem toda a documentação e ajuda da xAI foi examinada, isso não permite afirmar categoricamente que esse comportamento não esteja documentado em lugar nenhum
- O que se pode afirmar é que isso não aparecia no material de configuração do próprio CLI
~/.grok/upload_queuepodia colocar em staging cerca de 3GB de snapshot em um único turno e, sob carga elevada, crescer para dezenas de GB, podendo esgotar o disco- Trata-se de um problema de confiabilidade separado da questão de privacidade do upload
Configuração “Improve the model” e escopo de política
- É esperado que um agente de codificação em nuvem envie ao servidor o contexto de código necessário para executar a tarefa
- O comportamento confirmado no teste se divide em três pontos
- envio sem mascaramento de arquivos secretos como
.env - armazenamento desse conteúdo no bucket GCS especificado
- upload do repositório inteiro ativado por padrão, sem ficar evidente no material de configuração do CLI analisado
- envio sem mascaramento de arquivos secretos como
- A política de consumidor da xAI trata amplamente do uso de dados para melhoria do modelo e de opt-out; o Private Chat tem opt-out automático, e o opt-out não se aplica retroativamente
- Os documentos relacionados são xAI Privacy Policy e Consumer ToS
- Essas políticas gerais de treinamento não equivalem a documentar especificamente o pipeline de upload de
repo_statee GCS
-
Mesmo com “Improve the model” desativado, o upload não para
- Mesmo com a configuração desativada, o repositório inteiro foi enviado como git bundle para
/v1/storagee retornou HTTP 200 - Foi possível recuperar por
git clonearquivos não lidos e o histórico Git - O
/v1/settingsrecebido pelo CLI mantinha"trace_upload_enabled": true,"upload_enabled": truee"session_registry_enabled": true - Também retornava
"max_upload_file_bytes": 1073741824, limite de 1GiB por arquivo - Nos resultados do teste, o opt-out controlou a participação em treinamento, mas não bloqueou o comportamento de o repositório sair do computador para upload e armazenamento
- Mesmo com a configuração desativada, o repositório inteiro foi enviado como git bundle para
O que não foi comprovado e limitações da evidência
- Apenas a captura de rede não permite comprovar que a xAI use os dados para treinar modelos
- O que foi confirmado foi transmissão, aceitação com HTTP 200, arquivo de armazenamento e destino GCS
- Os logs dos PUTs diretos para
storage.googleapis.com/grok-code-session-traces, observados na execução de 3GB, foram sobrescritos e não foram preservados- A evidência de upload de vários GiB vem dos logs preservados de
/v1/storagena execução de 12GB, além do binário e do metadata apontando o bucket
- A evidência de upload de vários GiB vem dos logs preservados de
- Nos testes por tamanho, os logs de 64MB, 600MB e 3GB não foram preservados, e apenas o log de 12GB foi mantido
- Como a execução de 12GB foi interrompida em cerca de 5.10GiB, não se pode afirmar que os 12GB completos seriam enviados até o fim
- Nem todas as combinações de plano e configuração de conta foram testadas
- No plano gratuito, um upload de vários GiB foi bem-sucedido
- No SuperGrok, o upload via git bundle também funcionou com “Improve the model” desativado
- Embora o teste não tenha encontrado uma configuração para desativar os uploads, isso não autoriza concluir que eles sejam impossíveis de desativar em qualquer ambiente
- Inicialmente, uma leitura por PID do
nettoplevou à conclusão errada de que blobs grandes não estavam sendo enviados, mas isso foi posteriormente retirado- Um processo separado de coordenação de uploads e PUTs pré-assinados indo diretamente para IPs do Google poderiam não aparecer em medições por host de API ou por PID único
- A captura de wire via proxy posterior substitui essa avaliação inicial
- Os resultados se limitam a
grok 0.2.93, macOS Apple Silicon e ao contexto de julho de 2026, e a xAI pode alterar esse comportamento depois
Principais evidências preservadas
secrets_responses_body.bin: mostra que o conteúdo bruto de.envestava incluído no corpo de/v1/responsessecrets_session_state.tar.gz: mostra que os mesmos valores secretos estavam no arquivo destinado a/v1/storagewire_12gb.log: registra 5.10GiB em uploads de armazenamento, 83 HTTP 200 em/v1/storage*, zero falhas de armazenamento e a diferença de cerca de 27.800 vezes entre os dois canaismodel_limit.txt: registra 1 erro 402 e 3 erros 429 nas requisições ao modelocrate_strings.txt: preserva as stringsxai-data-collector,grok-code-session-tracesestorage.googleapis.comuploaded_repo.bundle: primeira evidência de repositório mostrando recuperação de arquivo não lido e de todo o histórico Git a partir do git bundle enviadouploaded_repo_auth.bundle: evidência de reprodução do mesmo resultado em um segundo repositório independentestaged_base_tree_manifest.json: mostra que arquivos não lidos foram listados no manifest do snapshot do repositóriostaged_metadata.json: mostra que o destino dos arquivos erags://grok-code-session-traces/…gcs_puts.txté apenas um placeholder vazio porque os PUTs diretos ao GCS não puderam ser preservados e, portanto, não pode ser usado como evidência preservada
1 comentários
Comentários do Hacker News
Sempre separo a ferramenta de programação do provedor de LLM e limito as permissões da ferramenta com um sandbox bubblewrap
A ferramenta só pode ler o diretório do projeto em que está trabalhando,
.gitfica somente leitura, e diretórios sensíveis são montados como diretórios vaziosTambém isolo o namespace de rede para que a internet só seja acessível por um proxy HTTP em socket Unix, permitindo apenas hosts de provedores de LLM específicos e bloqueando o host da própria ferramenta
Por exemplo, no Crush permito acesso a
*.openrouter.ai, mas bloqueio*.charm.land, usado para atualizar automaticamente a lista de LLMs. Assim, fica muito mais confortável deixar tudo no modoyolodebian:unstabledo Docker Hub e montar um ambiente completo de distribuição em uma pasta separadaDepois de instalar o agente de IA lá dentro, basta criar um script para executar o
bwrap, deixando a rootfs da distribuição como somente leitura e um/home/userpersonalizado como leitura e escrita. Arquivos importantes fora dos diretórios especificados não ficam visíveis, e também dá para executar vários agentes sem que eles enxerguem uns aos outrosPara reforçar ainda mais, dá para chamar
runsc ... do ...do gVisor por dentro ou usar um monitor de máquina virtual como o muvm. Obwrapcuida da composição do ambiente, e o bloqueio fica a cargo de uma ferramenta de sandbox separada, o que o torna confiávelSe a configuração estiver correta, só o
bwrapjá basta para barrar a maioria dos atacantes, e para escalar privilégios provavelmente seria preciso um zero-day do kernel LinuxSe o modelo é burro a ponto de precisar ser contido por restrições, então na minha visão ele nem valeria a pena. Eu também estou reforçando meu próprio ambiente, então não é uma crítica à prática em si
Runtimes proprietários nativos de agentes de programação como claude-code, Codex e grok-build são arriscados do ponto de vista de privacidade, porque não há como saber que recurso fechado será adicionado na próxima atualização
Usar modelos via API no opencode parece muito mais seguro, mas há o custo de não conseguir o mesmo nível de desempenho de um runtime nativo
O método do Grok só é mais escancarado; no fim, o opencode também não cria uma fronteira de segurança real, sendo algo parecido com o meme de usar Cheetos como cadeado
É perigoso não corrigir imediatamente algo como uma vulnerabilidade de execução remota de código no Windows XP SP1, mas ao longo das últimas décadas vi mais danos causados por atualizações automáticas do que por falta de atualização
É muito chocante que “faça upload de todo o repositório, incluindo o conteúdo de todos os arquivos rastreados e o histórico do Git, independentemente dos arquivos que o agente realmente leu”
Eu até imaginava que o Elon pudesse fazer algo assim para tentar alcançar os outros, mas isso é seriamente preocupante. O preço é competitivo e o desempenho do grok-4.5 parece bom o bastante, mas foi exatamente por isso que eu não escolhi usar
O CLI pode acabar levando por engano uma chave SSH ou outra informação sensível, e programadores realmente cometem esse tipo de erro com frequência. Não quero depender de ser seguro ou não se “fazer upload de todos os arquivos acessíveis” foi intencional ou acidental
O primeiro item, “o modelo leu um arquivo com segredos dentro do repositório”, é na prática um comportamento esperado
Um LLM não consegue saber se um arquivo contém segredos antes de lê-lo. O problema fundamental é dar a um LLM acesso a arquivos com segredos em texto puro e depois se surpreender porque ele os leu
Dito isso, fazer upload automático do repositório inteiro é absurdo. Se o repositório tiver vários GB, isso pode levar muito tempo em algumas conexões e, a menos que haja outro objetivo de coletar todos os dados, em geral parece sem sentido
Eu sempre parti do princípio de que o workspace atual em que o agente está rodando fica, no mínimo, totalmente à disposição dele, então isso parece um comportamento esperado
A maioria dos agentes lê o código no prompt inicial, incluindo quaisquer segredos nele. Se usar isso no servidor reduz latência de ida e volta do prompt e chamadas de ferramenta, fico até em dúvida se isso não beneficia o usuário
Mas aqui foi encontrado um endpoint separado que exfiltra a pasta inteira do projeto para um bucket de storage do GCP. Quem já projetou sistemas distribuídos de grande escala reconhece uma arquitetura voltada a coletar dados de treino
Sem fazer upload de todos os arquivos, ele pode buscar só as partes relevantes e enviá-las para o modelo usar
Seria melhor se a visão geral tivesse sido escrita por uma pessoa, mas o conteúdo em si é preocupante
Relatórios escritos por IA são cansativos demais de ler; passei uns 10 segundos olhando e perdi o interesse
Fico me perguntando se o que foi roubado vai parar no projeto Macrohard de “automatizar todos os negócios” ou no “everything app”
Parece a lógica de que não é preciso construir tudo se dá para roubar
Se você administra uma empresa assim sem nenhum senso moral, tentará roubar o máximo possível antes que a escala da fraude fique clara e a regulação intervenha. Não estou dizendo que é isso que está acontecendo, mas os incentivos econômicos estão perfeitamente alinhados nessa direção
Deve-se assumir que o agente de IA pode ler os arquivos do diretório em que o executor foi iniciado
Na maioria dos casos, ele lê já no primeiro prompt o código e até os segredos contidos nele, e
.envé para ambiente local, então não deveria conter segredos reais. Como não dá para confiar nas instruções dadas a um agente de IA, ele deve ficar isolado dos segredos reaisSe aceitarmos essa premissa, talvez seja até melhor manter o código no servidor do que enviá-lo como contexto toda vez
Então, a única razão para esse upload separado seria Musk querer obter dados de treino limpos, como estrutura de projetos, bibliotecas populares e fluxos de trabalho de CI, para o próximo modelo
Todos os provedores têm capacidade e incentivo para fazer o mesmo se isso ajudar a melhorar os resultados
A diferença real é enviar arquivos de segredos como
.envsem mascará-los, e não só processá-los temporariamente, mas armazená-los em um bucket GCS nomeado, além de ativar isso por padrão sem informar o método de upload na documentação de configuração da CLINão se deve deixar um
.envsem criptografia em um caminho acessível. Seria melhor se o Grok identificasse e ignorasse segredos, mas o usuário não deveria depender desse comportamentoO fato de o repositório inteiro ser enviado exatamente da mesma forma com a configuração “Improve the model” ligada ou desligada é muito grave
A maioria das empresas de IA também faria algo parecido em seus próprios executores se houvesse consentimento para coleta de dados, mas continuar enviando mesmo depois de desativar explicitamente é malicioso
Ao fazer upload de toda a codebase, o modelo pode examinar o código enquanto “pensa” sem precisar pedir ao cliente chamadas reais de ferramentas
Não está claro qual seria a desvantagem de pedir isso de volta ao cliente, então não é uma justificativa muito boa, mas é a melhor explicação possível
Um código que antes era privado agora passa a ser deles
É algo bem útil, mas não útil o bastante para entregar o repositório inteiro ao Elon. O fato de terem tornado isso impossível de recusar e não terem divulgado nada só reforça a conclusão de que não se deve confiar esses dados a eles