Qué datos envía xAI Grok Build CLI a xAI: análisis a nivel de wire
(gist.github.com/cereblab)- Al capturar directamente el tráfico de red de
grok 0.2.93, se observó que Grok Build enviaba los archivos que leía sin enmascararlos y los guardaba comosession_state, además de incluir intactos valores secretos de prueba de.enven dos rutas. - Independientemente de que la solicitud al modelo enviara los archivos leídos por el agente, también se subió como
git bundletodo el repositorio, con todos los archivos rastreados y el historial de Git, y fue posible restaurar intactos incluso archivos que se había indicado no abrir. - En un repositorio con 12GB de archivos aleatorios, las solicitudes a
/v1/responsessumaron 192KB en total, pero la transferencia a/v1/storagellegó a 5.10GiB hasta el momento en que se detuvo la captura, una diferencia de unas 27,800 veces, y todas las solicitudes de almacenamiento devolvieron HTTP 200. - El destino de la subida era el bucket de Google Cloud Storage
grok-code-session-traces, y aunque se desactivara “Improve the model”,trace_upload_enabled: trueyupload_enabled: trueseguían activos y la subida del repositorio completo continuaba. - El experimento demuestra la transmisión, aceptación y almacenamiento de los datos, pero no pudo confirmar si se usaron para entrenar el modelo, y como no se probaron
.gitignoreni todas las combinaciones de cuentas y configuraciones, los resultados se limitan a una versión específica de julio de 2026.
Objeto de prueba y alcance del análisis
- El objetivo fue el Grok Build CLI oficial de xAI, iniciado sesión con una cuenta de consumidor común.
- La ruta de instalación era
~/.grok/bin/grok. - La autenticación se hacía en el navegador con una cuenta de X o SuperGrok, sin usar clave API.
- El binario probado era
grok 0.2.93 (f00f96316d4b)para Apple Silicon. - Su SHA-256 era
2a97ba675bd992aa9b981e2e83776460d94f469b510c0b8efe28b50d236d767c.
- La ruta de instalación era
- En las cadenas del binario se identificaron componentes internos de subida en Rust y constantes relacionadas con almacenamiento.
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 las capturas se limitaron a la computadora y al tráfico del autor de la prueba, y en el repositorio se colocaron cadenas canary únicas en lugar de credenciales reales.
Cómo reproducir y capturar el tráfico
- En macOS Apple Silicon, se agregó la CA de
mitmproxycomo certificado de confianza en el llavero de inicio de sesión y se configuraronHTTPS_PROXYySSL_CERT_FILEpara capturar las solicitudes HTTPS de Grok. - Grok no bloqueó la captura en esta configuración mediante certificate pinning.
- Con un addon de
mitmdumpse registraron el método, host, ruta, estado de respuesta y tamaño en bytes de cada solicitud, y se guardaron los cuerpos de las solicitudes dirigidas a hosts de xAI. - También se copiaron durante la ejecución los archivos de
~/.grok/upload_queue/*y se inspeccionaron los artefactos de staging pendientes de envío al descomprimirlos congzipytar. - En cada archivo se insertaron marcadores únicos para distinguir su origen, y en los archivos secretos se usaron valores falsos como estos:
API_KEY=CANARY7F3A9-SECRET-should-not-leaveDB_PASSWORD=CANARY7F3A9-DBPASS
Canal A: envío al modelo de archivos leídos y de .env
- El contenido de los archivos leídos por Grok se serializó en el cuerpo del turno del modelo en
POST cli-chat-proxy.grok.com/v1/responses.- La solicitud capturada de 48,070 bytes incluía
"model":"grok-4.5"y un arreglo de mensajes. - Los canary
API_KEYyDB_PASSWORDdel.envaparecieron intactos, incluidos los saltos de línea. - En el cuerpo de la solicitud se pudieron recuperar 6 marcadores, incluyendo código fuente, lógica, README, archivos JavaScript anidados y los dos valores secretos.
- La solicitud capturada de 48,070 bytes incluía
- Ese mismo contenido también entró al archivo
session_statey se subió porPOST /v1/storage, y esa solicitud de almacenamiento fue aceptada con HTTP 200.- Al extraer e inspeccionar el archivo comprimido, se encontraron nuevamente ambos valores secretos y otros marcadores de archivo.
- Los secretos no solo fueron procesados temporalmente por el modelo, sino que además quedaron registrados en un archivo para almacenamiento.
- Con el prompt de control
Reply exactly OK, do not read any files, el marcador deuntouched_secret.txt, que no había sido abierto, no apareció en el cuerpo de/v1/responses.- Esto solo demuestra que el canal de solicitud al modelo se construye alrededor de los archivos que el agente realmente leyó.
- Como existe un canal separado de snapshot completo del repositorio que sí incluye archivos no leídos, este resultado de control no niega la subida del repositorio completo.
- Los valores secretos estaban en
.envosecrets.envy eran archivos rastreados por Git.- No se probó aparte si también se suben archivos incluidos en
.gitignore. - Como los canary no tenían formato de token real de alta entropía, esto no demuestra por completo que no exista un mecanismo de enmascarado específico para patrones como
sk-…. - Lo confirmado es que el contenido de
.envprobado fue enviado sin enmascararse.
- No se probó aparte si también se suben archivos incluidos en
Canal B: subida del repositorio completo, incluyendo archivos no leídos
/v1/storagesubió un snapshot completo del espacio de trabajo por separado de/v1/responses, que envía los archivos que el modelo leyó.- Se ejecutó en el repositorio real
cereblab_apitras indicarReply with exactly: OK. Do not read or open any files..- Grok envió por
POST /v1/storageungit bundlede 152,102 bytes y el servidor devolvió HTTP 200. - Al hacer
git clonedeluploaded_repo.bundlecapturado, se restaurósrc/_probe/never_read_canary.txt, un archivo que se había indicado no abrir. - El marcador
CANARY-XR47P2-NEVERREAD-UNIQUEdentro del archivo también permaneció intacto. - El bundle incluía 4 commits, 47 archivos y todo el historial de Git.
- Grok envió por
- En un segundo repositorio independiente,
cereblab_auth, se reprodujo el mismo resultado.- Se subió por
POST /v1/storageungit bundlede 31,743 bytes y devolvió HTTP 200. - En el repositorio restaurado se confirmó intacto el marcador
CANARY-AUTH-4T8K2-NEVERREADde un archivo no leído.
- Se subió por
- En una ejecución separada de
cereblab_apicon 298 archivos, el manifest en staging enumeró también archivos no leídos.- Se registraron juntos
src/_probe/never_read_canary.txty código real comosrc/apns.tsysrc/index.ts. - El
fileIdpor archivo enmetadata.jsontenía la formags://grok-code-session-traces/repo_changes_dedup/v2/supplemental/sha256_…. - En esa ejecución no se completó la transferencia de blobs de contenido por una limitación de cuota, pero la subida real quedó confirmada en el experimento del
git bundleseparado y en la prueba de 12GB.
- Se registraron juntos
- El binario también contenía cadenas como
after_codebase.tar.gz,repo_state.upload,collecting workspace files,spawning background coordinator, coherentes con un mecanismo de recolección del espacio de trabajo.
Transferencia de múltiples GiB confirmada en un repositorio de 12GB
- Se observó el comportamiento de subida aumentando el tamaño del repositorio con archivos aleatorios casi incomprimibles.
- Con 64MB, un solo
POST /v1/storagede unos 48MB devolvió HTTP 200, pero no se conservaron los logs. - Con unos 600MB, varias decenas de chunks de unos 7.5MB devolvieron HTTP 200, pero tampoco se guardaron logs.
- Con unos 3GB, tras la inicialización multipart se observaron PUT de 50MB hacia
storage.googleapis.com, aunque no se conservaron logs directos de esos PUT. - Con unos 12GB, se siguieron enviando chunks de unos 75MB a
/v1/storage, y la captura se detuvo en 5.10GiB.
- Con 64MB, un solo
- El
wire_12gb.logconservado contiene 83 respuestas HTTP 200 a/v1/storage*.- 82
POST /v1/storagede subida de contenido. - De ellas, 73 eran chunks de unos 75MB.
- La suma de esos 73 chunks fue
5,476,083,317B, es decir, 5.10GiB o 5.48GB. - También incluyó 9 POST pequeños y 1 verificación de duplicados en
/v1/storage/batch_exists. - El total de bytes de las solicitudes
/v1/storage*fue5,476,228,005B, y no hubo ninguna solicitud de almacenamiento fallida.
- 82
- Como la captura se interrumpió mientras la subida seguía en curso, esto no demuestra que los 12GB completos se hayan subido.
- El último registro también fue un HTTP 200 para un chunk de unos 75MB.
- Lo demostrado es que se transmitieron con éxito más de 5.10GiB y que el volumen seguía aumentando al momento de detener la captura.
- Entre ambos canales de la misma sesión hubo una gran diferencia de volumen.
/v1/responsessumó196,705Ben 5 solicitudes, unos 192KB, y la más grande fue de 60,394B./v1/storagefue de 5.10GiB.- Se produjo una diferencia de aproximadamente 27,800 veces, lo que indica que una gran cantidad de datos del repositorio no incluidos en la solicitud al modelo se movió por un canal de almacenamiento separado.
- Los errores de cuota del modelo y la subida al almacenamiento ocurrieron de forma independiente.
- En
/v1/responseshubo 1 HTTP 402 y 3 HTTP 429. - En las solicitudes de bookkeeping de sesión hubo 1 HTTP 404 no relacionado.
- Incluso después del primer 429, siguieron 76 HTTP 200 de
/v1/storage. - Las 82 solicitudes de almacenamiento tuvieron éxito y, dentro del alcance de la prueba, no hubo errores por límite de capacidad de almacenamiento.
- En
- Este resultado no depende de que la
upload_queuelocal se vacíe.- La cola puede vaciarse tanto por una subida exitosa como por descarte, así que no se usó como evidencia.
- La base de la conclusión son las capturas donde los cuerpos de las solicitudes salieron realmente por la red y recibieron HTTP 200, junto con la restauración de archivos desde el
git bundlesubido.
Ubicación de almacenamiento y telemetría
- El destino de almacenamiento identificado no fue AWS S3, sino el bucket de Google Cloud Storage
grok-code-session-traces.- El binario contenía
grok-code-session-traces,storage.googleapis.com,Uploading bytes to GCS via proxy. - El
metadata.jsonconservado registró como destino de archivogs://grok-code-session-traces/…. - En el experimento de unos 3GB también se observaron PUT multipart directos a ese host de GCS, aunque no se conservaron logs.
- Aunque el binario incluye
aws-sdk-s3, el destino confirmado en la prueba fue GCS.
- El binario contenía
- También se confirmaron solicitudes de telemetría tanto de terceros como propias.
api.mixpanel.com/tracky/engagede Mixpanel.grok.com/_data/v1/events- Todas esas solicitudes devolvieron HTTP 200.
- En el script de instalación del CLI y en el material de quickstart revisados no se encontraron referencias a subidas de
repo_state,session_state,~/.grok/upload_queuenigrok-code-session-traces.- Como no se revisó toda la documentación y ayuda de xAI, no se puede afirmar categóricamente que no esté documentado en ninguna parte.
- Lo verificable es que no aparecía en el material de configuración del propio CLI.
~/.grok/upload_queuepodía hacer staging de snapshots de unos 3GB en un solo turno y, bajo carga alta, crecer hasta decenas de GB y agotar el disco.- Este es un problema de confiabilidad aparte del problema de privacidad de las subidas.
Ajuste “Improve the model” y alcance de la política
- Que un agente de codificación en la nube envíe al servidor el contexto de código necesario para trabajar es, en sí mismo, un comportamiento esperado.
- El comportamiento confirmado en la prueba se divide en tres puntos.
- Envía archivos secretos como
.envsin enmascarar. - Guarda ese contenido en el bucket de GCS especificado.
- La subida del repositorio completo está activada por defecto sin quedar expuesta en el material de configuración del CLI revisado.
- Envía archivos secretos como
- La política de consumidor de xAI trata de forma amplia el uso de datos para mejorar el modelo y la posibilidad de opt-out; Private Chat viene con opt-out automático y el opt-out no es retroactivo.
- Los documentos relacionados son xAI Privacy Policy y Consumer ToS.
- Estas políticas generales de entrenamiento no equivalen a documentar un pipeline específico de subida de
repo_statey GCS.
-
Desactivar “Improve the model” no detiene la subida
- Incluso con esa opción desactivada, todo el repositorio se subió como
git bundlea/v1/storagey devolvió HTTP 200. - Mediante
git clonese pudieron restaurar archivos no leídos y el historial de Git. - En
/v1/settings, recibido por el CLI, se mantuvieron"trace_upload_enabled": true,"upload_enabled": true,"session_registry_enabled": true. - También se devolvió
"max_upload_file_bytes": 1073741824, es decir, un límite de 1GiB por archivo. - Según el resultado de la prueba, el opt-out controla si se usa para entrenamiento pero no bloquea que el repositorio salga de la computadora ni su subida y almacenamiento.
- Incluso con esa opción desactivada, todo el repositorio se subió como
Qué no se demostró y límites de la evidencia
- Solo con la captura de red no se puede demostrar que xAI use los datos para entrenar el modelo.
- Lo confirmado es la transmisión, la aceptación con HTTP 200, los archivos de almacenamiento y el destino en GCS.
- Los logs de los PUT directos a
storage.googleapis.com/grok-code-session-tracesobservados en la ejecución de 3GB se sobrescribieron y no se conservaron.- La evidencia de la subida de múltiples GiB se basa en los logs preservados de
/v1/storagede la ejecución de 12GB, además del binario y metadata que especifican el bucket.
- La evidencia de la subida de múltiples GiB se basa en los logs preservados de
- De las pruebas por tamaño, no quedaron logs de 64MB, 600MB ni 3GB; solo se conservó el log de 12GB.
- La ejecución de 12GB se detuvo cerca de 5.10GiB, así que no puede afirmarse que los 12GB completos se suban hasta el final.
- No se probaron todas las combinaciones de tipo de cuenta y configuración.
- En el plan gratuito, la subida de múltiples GiB tuvo éxito.
- En SuperGrok, la subida del
git bundletambién tuvo éxito con “Improve the model” desactivado. - En la prueba no se encontró una opción para desactivar la subida, pero eso no permite afirmar que sea imposible desactivarla en cualquier entorno.
- Al inicio se concluyó erróneamente, a partir de resultados de
nettoppor PID, que no se estaban subiendo blobs grandes, pero esa conclusión fue retirada.- Un proceso separado de coordinación de subida y los PUT prefirmados dirigidos directamente a IPs de Google podían quedar fuera de mediciones basadas en el host API o en un solo PID.
- La captura de wire a través del proxy reemplaza esa evaluación inicial.
- Los resultados se limitan a
grok 0.2.93, macOS Apple Silicon y al entorno de julio de 2026, y xAI podría cambiar ese comportamiento posteriormente.
Evidencia principal conservada
secrets_responses_body.bin: muestra que el.enven texto plano estaba incluido en el cuerpo de/v1/responses.secrets_session_state.tar.gz: muestra que esos mismos valores secretos estaban dentro del archivo para/v1/storage.wire_12gb.log: registra la subida de 5.10GiB al almacenamiento, 83 HTTP 200 de/v1/storage*, 0 fallas de almacenamiento y la diferencia de volumen de unas 27,800 veces entre ambos canales.model_limit.txt: registra 1 error 402 y 3 errores 429 en las solicitudes al modelo.crate_strings.txt: conserva las cadenasxai-data-collector,grok-code-session-traces,storage.googleapis.com.uploaded_repo.bundle: primera evidencia en un repositorio de que desde elgit bundlesubido se restauraron archivos no leídos y todo el historial de Git.uploaded_repo_auth.bundle: evidencia de que el mismo resultado se reprodujo en un segundo repositorio independiente.staged_base_tree_manifest.json: muestra que archivos no leídos estaban enumerados en el manifest del snapshot del repositorio.staged_metadata.json: muestra que el destino de los archivos erags://grok-code-session-traces/….gcs_puts.txtes un placeholder vacío porque no se pudieron conservar los PUT directos a GCS, y no puede usarse como evidencia preservada de esos PUT.
1 comentarios
Opiniones en Hacker News
Siempre separo las herramientas de programación de los proveedores de LLM, y limito los permisos de las herramientas de programación con un sandbox bubblewrap
La herramienta solo puede leer el directorio del proyecto de trabajo,
.gitqueda como solo lectura, y los directorios sensibles se montan como directorios vacíosTambién aíslo el namespace de red para que solo acceda a internet mediante un proxy HTTP en un socket Unix, y permito únicamente hosts de proveedores de LLM específicos, bloqueando los hosts de la propia herramienta
Por ejemplo, a Crush le permito acceder a
*.openrouter.ai, pero bloqueo*.charm.land, que se usa para actualizar automáticamente la lista de LLM. Gracias a eso, es mucho más cómodo dejarle todas las tareas en modoyolodebian:unstablede Docker Hub y configurarlo en una carpeta aparte como un entorno de distribución completoLuego instalas ahí el agente de IA y creas un script para ejecutar
bwrap, dejando el rootfs de la distribución como solo lectura y un/home/userpersonalizado como lectura-escritura. Los archivos importantes fuera de los directorios especificados no se ven, y también puedes ejecutar varios agentes sin que se vean entre síPara reforzarlo más, puedes invocar desde dentro
runsc ... do ...de gVisor o usar un monitor de máquina virtual como muvm.bwrapse encarga de configurar el entorno y otra herramienta de sandbox lo bloquea, así que es un enfoque confiableSi la configuración es correcta, con
bwrapsolo debería bastar para detener a la mayoría de los atacantes, y para escalar privilegios probablemente tendrían que usar, en la práctica, un zero-day del kernel de LinuxSi un modelo hace cosas tan tontas que hay que impedirlas con restricciones, creo que para empezar no vale la pena usarlo. Yo también estoy reforzando mi propio entorno, y no pretendo criticar la práctica en sí
Los ejecutores nativos propietarios de agentes de programación como claude-code, Codex y grok-build son un riesgo para la privacidad porque no se sabe qué funciones privadas se agregarán en la próxima actualización
Usar modelos mediante API en opencode es mucho más seguro, pero implica el compromiso de que es difícil lograr un rendimiento tan bueno como el de los ejecutores nativos
El enfoque de Grok simplemente es más descarado; opencode tampoco crea una frontera de seguridad real, es parecido al meme de usar Cheetos como candado
No aplicar de inmediato parches para cosas como vulnerabilidades de ejecución remota de código de Windows XP SP1 también es riesgoso, pero en las últimas décadas he visto más daños causados por actualizaciones automáticas que daños que probablemente se habrían producido por no actualizar
Que “suba todo el repositorio, incluidos los contenidos de todos los archivos rastreados y el historial de Git, independientemente de los archivos que leyó el agente” es muy impactante
Hasta cierto punto esperaba que Elon pudiera hacer algo así para ponerse al día, pero es seriamente preocupante. Tiene precios competitivos y el rendimiento de grok-4.5 también es bastante bueno, pero precisamente por esto no lo elegí
La CLI podría llevarse por accidente claves SSH u otra información sensible, y los programadores de hecho cometen estos errores con frecuencia. No quiero dejar mi seguridad en manos de si “subir todos los archivos accesibles” es intencional o accidental
El primer punto, que “el modelo leyó un archivo con secretos dentro del repositorio”, es básicamente el comportamiento previsto
Un LLM no puede saber si un archivo contiene secretos antes de leerlo. El problema de fondo está en sorprenderse porque el LLM leyó un archivo de texto plano con secretos al que se le dio acceso
Dicho eso, subir automáticamente todo el repositorio es absurdo. Si es un repositorio de varios GB, en algunas conexiones tardaría muchísimo y, salvo que haya otro objetivo de recolectar todos los datos, en general parece inútil
Siempre asumí que el espacio de trabajo actual donde se ejecuta el agente, como mínimo, podía ser manejado libremente por el agente, así que parece un comportamiento esperado
La mayoría de los agentes leen el código y hasta los secretos que contiene en el primer prompt. Si el servidor usa eso para reducir los viajes de ida y vuelta del prompt y las llamadas a herramientas, me pregunto si no sería incluso beneficioso para el usuario
Pero aquí se encontró un endpoint separado que extrae toda la carpeta del proyecto hacia un bucket de almacenamiento de GCP. Cualquiera que haya diseñado sistemas distribuidos a gran escala puede ver que es una arquitectura para raspar datos de entrenamiento
Sin subir todos los archivos, puede encontrar mediante búsqueda las partes relevantes y enviarlas para que el modelo las use
Habría sido bueno que una persona escribiera el resumen, pero el contenido en sí es inquietante
Los informes escritos por IA son tan pesados de leer que lo hojeé unos 10 segundos y perdí el interés
Me pregunto si lo robado irá al proyecto Macrohard que “automatiza todos los negocios” o a la “everything app”
Parece la idea de que no hace falta construirlo todo uno mismo si se puede robar
Si dirigiera una empresa así sin moral, intentaría robar todo lo posible antes de que se revele la escala de la estafa y la regulación la frene. No digo que eso sea lo que efectivamente estén haciendo, pero los incentivos económicos están alineados exactamente en esa dirección
Hay que asumir que un agente de IA puede leer los archivos del directorio desde el que se inició el ejecutor.
En la mayoría de los casos, en el primer prompt lee el código e incluso los secretos dentro de él, y como
.enves para el entorno local, no debería contener secretos reales. Como no se puede confiar en las instrucciones de un agente de IA, hay que aislarlo de los secretos reales.Si aceptamos esa premisa, quizá sea mejor subir el código al servidor que enviarlo cada vez como contexto.
Creo que la única razón para subirlo por separado así es que Musk quiere conseguir datos de entrenamiento limpios para el próximo modelo, como la estructura de los proyectos, las bibliotecas populares y los flujos de trabajo de CI.
Todos los proveedores tienen la capacidad y el incentivo para hacer lo mismo si eso ayuda a mejorar los resultados.
La verdadera diferencia es que envía archivos con secretos como
.envsin excluirlos, no se limita a procesarlos temporalmente sino que los guarda en un bucket de GCS con nombre, y lo activó por defecto sin informar en la documentación de configuración de la CLI cómo funciona la subida.No se debe dejar un
.envsin cifrar en una ruta accesible. Sería mejor que Grok identificara los secretos y los ignorara, pero los usuarios no deberían depender de ese comportamiento.Es muy grave que todo el repositorio se suba igual, independientemente de si la configuración “Improve the model” está activada o desactivada.
La mayoría de las empresas de IA probablemente harían algo parecido en sus propios ejecutores si uno acepta la recopilación de datos, pero subirlo aun cuando se desactivó explícitamente es malicioso.
Si se sube toda la base de código, el modelo puede revisar el código mientras “piensa”, sin pedirle al cliente llamadas reales a herramientas.
No está claro cuál sería la desventaja de volver a pedírselo al cliente, así que no es una razón muy convincente, pero es la mejor justificación que se me ocurre.
El código que antes era privado ahora pasa a ser de ellos.
Es bastante útil, pero no tanto como para entregarle todo el repositorio a Elon. El hecho de que lo hayan hecho imposible de rechazar y no lo hayan revelado en absoluto refuerza aún más la conclusión de que no se les debe confiar estos datos.