Volver a todas las publicaciones
Publicado el · por Renaud Deraison

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.

WORKSPACE — el repo clonadoproject_settings.jsonparece configuración — es un symlink"Actualiza project_settings.json"— el README del repo, obedecido por el agenteLÍMITE DEL WORKSPACEse asume seguro — la suposición es el bugHOME DEL DESARROLLADOR (~) — fuera del repo~/.ssh/authorized_keys~/.zshrc~/.aws/credentialsacceso real al host · claves de nube reales · shell que corre en cada loginel agente sigue el enlaceCWE-451 — EL AVISO TERGIVERSA LO QUE ESTÁ A PUNTO DE PASARDestino real del agente: ~/.ssh/authorized_keysLo que ves: "¿Editar project_settings.json? [Sí]"
Cómo funciona GhostApproval. Un repositorio clonado contiene un symlink disfrazado de archivo de configuración; apunta fuera del workspace, hacia el directorio personal del desarrollador. Cuando el agente lo edita, la escritura aterriza en ~/.ssh/authorized_keys, ~/.zshrc o ~/.aws/credentials. Peor aún, en varios productos el aviso de confirmación mostraba el nombre inofensivo que le pidieron editar al agente, no la ruta sensible que estaba a punto de tocar (CWE-451).

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.

Agente en tu MacUNA MÁQUINA, UN HOMEagente + repo clonadosymlink → ~/.ssh/authorized_keys~/.ssh/authorized_keysREAL~/.aws/credentialsREAL~/.zshrcREALResultadoclave SSH del atacante instalada,claves de nube leídas, shell secuestrada —en la máquina desde la que trabajas.Bromure Agentic CodingVM POR PERFIL — DESECHABLEagente + repo clonadosymlink → ~/.ssh/authorized_keysla escritura ocurre igual~/.ssh/authorized_keysSTUB~/.aws/credentialsSTUB~/.zshrchuéspedResultadouna clave añadida a un huésped desechable,claves de nube de mentira exfiltradas —tu Mac nunca entró en la sala.
El mismo escape por symlink, dos máquinas. En una configuración normal el agente corre junto a tus ~/.ssh y ~/.aws reales, así que seguir el enlace alcanza claves vivas y una máquina que vale la pena tomar. En Bromure el agente corre en una VM por perfil; el symlink se sigue resolviendo y la escritura sigue ocurriendo, pero aterriza en un huésped desechable cuyo directorio personal solo contiene stubs: archivos marcadores de posición que no significan nada en la internet pública. El sistema de archivos de tu Mac queda bajo la línea del hipervisor y nunca se monta en la VM.

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.