El symlink salió del workspace, y no encontró nada que llevarse
GhostApproval mostró cómo un repositorio hostil puede engañar a seis asistentes de codificación con IA para que escriban y lean archivos fuera de la carpeta en la que debían quedarse: plantando claves SSH, secuestrando el arranque de la shell y leyendo credenciales de la nube en la propia máquina del desarrollador. El bug es un error al seguir symlinks. La razón por la que duele es que el agente corre en una máquina llena de tus secretos. Bromure Agentic Coding cambia ese segundo hecho.
Un agente de codificación es un programa al que invitas a leer y escribir archivos en tu portátil. GhostApproval es la historia de lo que pasa cuando un repositorio hostil convence a ese programa de escribir un poco fuera de las líneas: en tus claves SSH, en la configuración de tu shell, en tus credenciales de la nube. El bug es viejo y aburrido. El daño viene de dónde estaba parado el agente.
El 8 de julio de 2026, el equipo de investigación de Wiz publicó GhostApproval, un análisis del mismo fallo encontrado, en seis formas distintas, en seis asistentes de codificación con IA populares: Amazon Q Developer, Claude Code de Anthropic, Augment, Cursor, Antigravity de Google y Windsurf. Los detalles varían por producto. La forma no. Abres un repositorio, quizá un tutorial que alguien enlazó o un proyecto que un compañero te pidió mirar, y apuntas tu agente de codificación hacia él. En algún lugar de ese repositorio hay un archivo que no es un archivo.
Un archivo que no es un archivo.
El truco es un enlace simbólico, o symlink: una entrada del sistema de archivos que parece un archivo pero es una señal que apunta a otra ruta. Los symlinks son una función normal de Unix con cuarenta años de historia. El problema de seguridad, catalogado como CWE-61, es casi igual de viejo: un programa que abre un symlink sin comprobar adónde apunta deja que un atacante lo dirija hacia un archivo que el autor nunca pretendió tocar.
En GhostApproval, un repositorio malicioso incluye un symlink con un
nombre de aspecto inocente, digamos project_settings.json. En lugar
de configuración, contiene un puntero a ~/.ssh/authorized_keys, o
~/.zshrc, o ~/.aws/credentials, que viven en el directorio personal
del desarrollador, bien fuera de la carpeta del repositorio. Un
README, o el usuario siguiendo las instrucciones del repo, pide
entonces al agente que "actualice project_settings.json". El agente
sigue la señal sin resolver adónde lleva, y escribe en el destino real.
Dos cosas hacen de esto algo más que una curiosidad. La primera es lo
que hacen los destinos. Añadir una línea a ~/.ssh/authorized_keys
instala la clave de un atacante para entrar por SSH a tu máquina sin
contraseña. Escribir en ~/.zshrc ejecuta el comando del atacante cada
vez que abres un terminal. El agente de Augment ni siquiera necesitó
escribir: al preguntarle por "las claves de AWS del proyecto", siguió
un symlink hasta un archivo de credenciales e imprimió los secretos en
el chat, sin aviso alguno.
La segunda es un fallo más sutil que Wiz clasifica como
CWE-451,
tergiversación de la interfaz de usuario. Con Claude Code, el propio
razonamiento interno del agente reconoció el archivo como configuración
de shell, y aun así el diálogo de confirmación solo preguntaba "¿Hacer
esta edición a project_settings.json?". El único momento en que un
humano podría haber detectado el peligro le mostró al humano la
información equivocada. Windsurf y Amazon Q Developer lo tenían peor:
escribían el archivo en disco antes de mostrar los botones de
aprobación, así que el diálogo era un aviso de deshacer y no una
compuerta. La clave SSH del atacante ya estaba instalada.
La mayoría de los proveedores ya han corregido su variante: Cursor en la v3.0 (CVE-2026-50549), Amazon Q en el language-server 1.69.0 (CVE-2026-12958), Google en Antigravity v1.19.6, Claude Code con una advertencia de resolución de symlinks. Es la respuesta correcta, y deberías instalar las actualizaciones. Pero parchear el manejo de symlinks de cada producto arregla el mecanismo y deja lo que hay en juego donde estaba.
Lo que hay en juego lo decide dónde está parado el agente.
Cada uno de estos payloads asume lo mismo: que llegar al directorio
personal del desarrollador vale la pena. Que ~/.ssh guarda una clave
que abre una máquina real, que ~/.aws/credentials guarda un token de
nube vivo, y que ~/.zshrc corre en una shell que abrirás mañana. El
symlink es el camino. El destino es un portátil con toda tu vida de
trabajo dentro.
La mayoría de los ataques a agentes de codificación descansan sobre la misma suposición. El agente es un programa capaz y abierto que lee entrada no confiable (repositorios, páginas web, tarballs de paquetes, salida de herramientas) y actúa en una máquina donde tus credenciales están en archivos planos a unos pocos directorios de distancia. Endurece el agente cuanto quieras; comparte un sistema de archivos y un llavero con todo lo demás que ejecutas. Los atacantes seguirán pavimentando caminos nuevos. El destino es el problema.
Bromure Agentic Coding elimina el destino. El agente, las shells que genera, los repositorios que clona y cualquier código dentro de ellos corren dentro de una VM Linux por perfil: una máquina separada que el hipervisor aísla de tu Mac. Tus credenciales reales nunca entran en ella. Escribimos sobre por qué una frontera vence a una jaula en por qué Bromure Agentic Coding no es un sandbox; GhostApproval es una prueba limpia de la idea, porque escapa de la carpeta del workspace, la única pequeña frontera en la que confiaba la industria.
Sigue el mismo escape dentro de la VM. El symlink se resuelve igual
(Bromure nunca necesita detectar ni bloquear el truco) y la escritura
sigue aterrizando en ~/.ssh/authorized_keys. Pero ese archivo vive en
el directorio personal del huésped, en una máquina desechable sin
ruta alguna hacia tu Mac. La clave del atacante ahora da acceso a una
máquina que vas a tirar, una que nunca fue alcanzable desde fuera en
primer lugar.
La variante de robo de credenciales es aún más anticlimática. Cuando un
agente comprometido sigue un symlink hasta ~/.aws/credentials e
imprime el contenido en el chat, lee un stub: un archivo de
credenciales bien formado cuyas claves no autentican nada. Los tokens
reales se quedan en el host, detrás de un broker que sustituye el
marcador de posición por el secreto real solo en el momento de una
llamada saliente que permitiste, y lo vuelve a sacar de la respuesta.
Una dependencia o un agente engañado que barre el sistema de archivos
en busca de secretos encuentra mobiliario.
El aviso, y la segunda red.
Bromure responde a los dos filos más agudos de GhostApproval en capas distintas.
El primero es el problema del aviso de confirmación, CWE-451: el
diálogo que decía project_settings.json mientras el agente sabía que
significaba ~/.zshrc. Bromure elimina el diálogo de escritura de
archivos; las escrituras dentro de la VM desechable tocan un disco
prescindible y no necesitan aprobación. En su lugar, Bromure inserta al
humano en la frontera real, para las llamadas que cambian el estado y
salen de la VM: un git push, un DROP de base de datos, un
Terminate en la nube. Esas se detienen en el host y te muestran la
operación literal en lugar de un resumen escrito por el agente. Es el
mismo principio que GhostApproval violó, aplicado donde importa: el
momento en que una decisión tiene consecuencias fuera del sandbox es el
momento en que un humano debería ver la llamada real.
El segundo filo es la instrucción que arranca todo el ataque: un
README que le dice al agente que "actualice project_settings.json",
y que el modelo lee como contexto y obedece como una orden. Bromure lee
primero ese contenido no confiable, en el dispositivo: un clasificador
local puntúa los archivos entrantes, las descargas web y la salida de
herramientas en busca de instrucciones que no pintan nada ahí, y los
archivos de reglas que un agente trata como órdenes permanentes reciben
un examen más severo. El clasificador se perderá algunas formulaciones,
y el aislamiento derrota este ataque por sí solo; trátalo como una
segunda red bajo la primera.
Donde la línea no te salva.
El aislamiento es una forma concreta, y la forma tiene bordes.
Un perfil es longevo, así que la persistencia persiste.
El payload de ~/.zshrc se ejecutará la próxima vez que se abra una
shell dentro de la VM de ese perfil. Corre en un huésped sin
claves del host y solo con credenciales stub, una shell en una sala
vacía, pero corre. Si eso te preocupa para una tarea concreta,
ejecútala en un perfil nuevo.
Lo que montas, lo expones.
La VM no contiene nada tuyo por defecto. Si montas deliberadamente un directorio real del host en un perfil, o pegas un token vivo en el huésped, un escape por symlink puede alcanzarlo. La línea mantiene tus secretos fuera; volver a meterlos es una decisión que solo tú puedes tomar.
Acota el broker a propósito.
Los stubs significan que el archivo en disco no vale nada, pero el broker sigue guardando tokens reales para las llamadas que permites. Un perfil que solo necesita leer un repo no debería llevar un token que pueda publicarlo. El aislamiento contiene la explosión; el alcance decide cuán grande podría llegar a ser.
El workspace era el muro equivocado.
GhostApproval es, en el fondo, una historia sobre lo que la industria
acordó tratar como frontera: la carpeta del workspace, la idea de que
"el agente se queda en el directorio del proyecto" es una propiedad de
seguridad y no un valor por defecto que cualquier señal puede rodear.
El symlink en sí es un bug que los proveedores pueden parchear, y lo
hicieron. Traza tu frontera en una carpeta y pasarás la eternidad
demostrando que ningún symlink, ningún .., ninguna ruta ingeniosa
sale jamás de ella. Trázala en el hipervisor y la pregunta deja de
importar, porque el lugar al que escapa el agente es una máquina
desechable que nunca contuvo nada tuyo.
Instala los parches de los proveedores; cierran un agujero real. Pero el agujero dolía porque el agente estaba parado en tu portátil, a un directorio de tus claves. Saca el agente de tu portátil y el próximo escape (habrá uno) llegará a una sala vacía. Bromure Agentic Coding es gratuito, de código abierto, y disponible hoy.