Se publicó esta semana y se llama Plugin4Shell. Afectaba a los cuatro agentes de programación con IA que los investigadores describen como los más usados —Claude Code, Codex, GitHub Copilot y Gemini CLI—, y el fallo está en dos órdenes de git, no en el modelo ni en los permisos que te pide la herramienta al arrancar. El agente clona el repositorio del plugin, hace checkout del commit exacto que el catálogo tenía fijado, y nunca comprueba que haya aterrizado ahí. Hemos reproducido esta tarde, en nuestro laboratorio y con git 2.43, el comportamiento en el que se apoya: la orden termina con código de salida 0, imprime un aviso que ningún automatismo lee, y deja en el directorio de trabajo el contenido del atacante.
Leído por encima parece un problema de herramientas de desarrollo, de esos que se quedan en el equipo de producto. Lo que lo saca de ahí es la lista de lo que un plugin alcanza cuando se ejecuta: el código fuente en local, las claves SSH del portátil, las credenciales de nube que hay en el entorno, los repositorios internos y lo que esas credenciales abran en producción. Un plugin corre con los permisos de quien ejecuta el agente. En la mayoría de empresas de software que conocemos, esos permisos son bastante más amplios de lo que nadie tiene escrito.
Qué han publicado exactamente
El hallazgo es de Air Security, que dice haber construido una prueba funcional en mayo de 2026, haberlo comunicado a los cuatro fabricantes en junio y haberlo hecho público esta semana. El mecanismo lo resumen así: «the agent checks out the exact commit the marketplace pinned but never verifies it landed there, so an attacker who controls the plugin's repo makes the checkout resolve to malicious code while the pin still looks honored». Y sobre lo que hace falta por parte de la víctima: «No user interaction of any kind is required. The result is full compromise of the agent and the host it runs on.»
La explicación de OpenAI recogida por la prensa técnica va al hueso del asunto sin adornos: git «can interpret a requested commit SHA as a branch name», y por eso el origen de un plugin puede «materialize a different commit than the one it pinned». Ahí está todo. Fijar el commit es la práctica correcta —es lo que hacemos todos para que una dependencia no cambie bajo los pies—, y sigue siéndolo. Lo que falla es el paso siguiente, que nadie da: confirmar que lo que hay delante es lo que pediste.
Lo hemos montado en el laboratorio
Antes de escribir nada quisimos ver el comportamiento con nuestros ojos, porque un titular así se repite mal con facilidad. Lo que reproducimos es el comportamiento de git sobre el que se apoya el ataque, no el código de los agentes. Un repositorio, dos commits y un nombre de rama elegido con mala idea.
# commit legítimo: 5c61cf42... commit del atacante: 536fe217...
git branch 5c61cf4299f1e060978d6676edc410ebe8cbbfd1 536fe217a392034ee6c1e42ac2621841469b5d45
git checkout 5c61cf4299f1e060978d6676edc410ebe8cbbfd1
La rama se llama igual que el commit que el catálogo tenía fijado, y apunta al commit del atacante. Esto es lo que contesta git:
warning: refname '5c61cf4299f1e060978d6676edc410ebe8cbbfd1' is ambiguous.
Git normally never creates a ref that ends with 40 hex characters
because it will be ignored when you just specify 40-hex. [...]
Switched to branch '5c61cf4299f1e060978d6676edc410ebe8cbbfd1'
El fichero del directorio de trabajo contiene lo que puso el atacante. El aviso es elocuente, y además se contradice con lo que acaba de pasar: dice que un ref así «será ignorado», y sin embargo git ha cambiado a la rama. Va por el canal de error, no rompe nada y devuelve cero. Cualquier automatismo que mire el código de salida —que es lo que mira un automatismo— sigue adelante tan contento.
Un matiz que no nos ahorramos, porque es donde esto se cuenta mal: en nuestro laboratorio, cuando la rama de cuarenta hexadecimales existía solo como rama remota del clon (refs/remotes/origin/…) y el commit legítimo estaba en el repositorio, git checkout aterrizó en el commit bueno. El truco necesita que el ref exista en local —porque es la rama por defecto del clon, como en la prueba siguiente— o que el commit fijado no esté en ninguna parte. Así que la prueba de arriba enseña la ambigüedad; la que viene ahora es la que describe el ataque.
La segunda prueba es todavía menos vistosa y más incómoda. Montamos un repositorio en el que el commit fijado no existe, con la rama trampa como rama por defecto. Al clonar, el clon se sitúa solo en esa rama; y el checkout del commit fijado no da error: responde Already on 'aaaa…' y sale con cero. El agente pidió un commit que no está en ningún sitio y se quedó con el código del atacante, sin una sola línea roja por el camino.
Y el detalle que nos parece más peligroso para quien audite esto a posteriori: en el primer escenario, con la rama trampa creada y el commit bueno presente, git rev-parse 5c61cf42… devuelve el commit legítimo y git log -1 --format=%s 5c61cf42… imprime «commit legitimo». La misma cadena de 40 caracteres da una respuesta distinta según qué orden de git la lea. Quien verifique con git log verá exactamente lo que espera ver mientras el directorio de trabajo tiene otra cosa dentro.
La comprobación que proponen los investigadores sí resuelve el caso, porque pregunta por HEAD y no por la cadena:
test "$(git rev-parse HEAD)" = "<pinned-sha>" || abort
La hemos ejecutado en las dos pruebas y en las dos aborta, porque devuelve el commit del atacante en vez del fijado. Hace falta en más sitios que el agente: cualquier pipeline de integración continua propio que clone un repositorio y haga checkout de un commit fijado tiene la misma pieza que falta. Esto no lo inventaron los agentes de IA; lo han puesto a correr millones de veces al día.
Dónde no se puede hacer, y dónde sí
Aquí el asunto deja de ser una anécdota de git y pasa a ser una decisión de infraestructura. El truco necesita que alguien pueda crear una rama cuyo nombre sean cuarenta caracteres hexadecimales, y las dos grandes plataformas de alojamiento no lo permiten. GitHub lo declaró a The Register: «GitHub does not allow users to create branch or tag names that resemble commit SHAs», y remata que «this mitigation ensures the reported vulnerability cannot be exploited on GitHub». GitLab lo tiene escrito en su documentación de ramas, en una línea seca: «Branch names with 40 hexadecimal characters are prohibited, because they are similar to Git commit hashes.»
La investigación señala como expuestos otros alojamientos que sí admiten esos nombres, y cita Bitbucket y los servidores git propios de cada empresa. Hoy, para este fallo concreto, quien está protegido lo está por dónde vive el repositorio, no por lo bien configurado que tenga el agente. Y el servidor git que montaste tú hace tres años, ese que nadie mira porque «solo es un remoto interno», no trae esa comprobación de fábrica.
Eso se arregla en diez minutos y sin comprar nada. Un hook de pre-recepción en el servidor, que rechace el push de cualquier rama con ese patrón. Lo hemos probado en el laboratorio contra un repositorio desnudo (bare): el push se rechaza y el mensaje llega al cliente.
remote: rechazado: nombre de rama con 40 caracteres hexadecimales
! [remote rejected] aaaaaaaa… -> aaaaaaaa… (pre-receive hook declined)
Un fallo, cuatro respuestas
Los cuatro fabricantes recibieron el aviso en junio. Esto es lo que consta a día de hoy, según la publicación de los investigadores y las respuestas que ha recogido la prensa técnica:
- Anthropic (Claude Code): corregido en la versión 2.1.179.
- OpenAI (Codex): corregido en la versión 0.146.0, con una explicación pública del porqué.
- Microsoft (GitHub Copilot): sin parche. Los investigadores dicen haberlo notificado en junio y no haber tenido respuesta, y atribuyen el silencio al volumen de comunicaciones que reciben. La exposición viene de admitir catálogos alojados en sitios que sí permiten esos nombres de rama.
- Google (Gemini CLI): no se va a corregir. La herramienta está retirada —Google dejó de servir la versión de consumo en junio— y la recomendación es migrar a Antigravity, que según los investigadores queda fuera del alcance de este ataque.
Un dato que sí hemos comprobado nosotros, porque es público y se consulta en un segundo: en el registro de npm, la versión 2.1.179 de Claude Code se publicó el 16 de junio de 2026 y la 0.146.0 de Codex el 29 de julio. Hoy van por la 2.1.278 y la 0.155.1, publicadas ayer y anteayer. Quien actualice sus herramientas con normalidad lleva meses con esto corregido y se ha enterado esta semana de por qué importaba; quien tenga una versión clavada del primer semestre, no. La comprobación de qué versión corre cada puesto lleva menos tiempo que leer este párrafo.
Nos interesa más esta lista que el fallo. El fallo es el mismo para los cuatro, y es un descuido honesto: la clase de cosa que cualquiera de nosotros habría escrito igual. Lo que separa a unos de otros es qué pasó después de recibir el correo, y eso sí es un dato de compra. Cuando una herramienta entra en el flujo de trabajo de tu equipo, entra también el historial de respuesta de quien la mantiene. «Sin parche tres meses después» y «no lo arreglamos, cambia de producto» son respuestas legítimas de un fabricante; la pregunta es si tú las habrías aceptado de saberlo antes de adoptarla.
El portátil de desarrollo es parte del plano de control
La actualización automática de los plugins viene activada de serie en Claude Code y en Codex, según la investigación. Es una decisión razonable —así se distribuyen las correcciones— y a la vez es el camino por el que una versión comprometida llega a todos los puestos sin que nadie apruebe nada. Un despliegue silencioso, de los que solo se ven en el registro cuando ya hay que explicar algo.
Llevamos meses escribiendo alrededor de esta misma idea desde ángulos distintos. Cuando hablamos de qué nombre acaba en el registro cuando un agente usa una credencial prestada, el asunto era la identidad. Cuando contamos que bloquear el agente SSH desactivaba sus propias restricciones, el asunto era la herramienta que sostiene las llaves. Aquí es el canal por el que llega el código. Las tres veces, el mismo objeto en el centro: una máquina de trabajo que puede hablar con producción y que no aparece en ningún inventario de sistemas críticos.
Lo que haríamos esta semana
- Actualizar lo que tiene arreglo: Claude Code a 2.1.179 o posterior, Codex a 0.146.0 o posterior. Es el único punto de esta lista que se hace en un minuto.
- Saber qué agentes hay y de qué catálogo cargan plugins. En muchas empresas la respuesta honesta es «lo que cada uno se haya instalado»; entonces la lista no existe y hay que hacerla antes de seguir.
- Mirar dónde se alojan esos repositorios. Si están en GitHub o en GitLab, la puerta está cerrada por el alojamiento. Si están en otro sitio o en un servidor propio, el hook de pre-recepción de arriba es la tarde de hoy.
- Meter la comprobación de una línea en cualquier automatismo propio que clone y haga checkout de un commit fijado. Empezando por la integración continua, que suele hacerlo con más permisos que el portátil.
- Preguntar qué abren las credenciales del portátil. Si la respuesta incluye producción con una llave sin caducidad, el problema de hoy es viejo y Plugin4Shell solo ha puesto la fecha.
- Decidir quién aprueba una actualización automática de plugins en equipos que tocan producción. Puede ser «nadie, que se actualice solo»; lo que no vale es no haberlo decidido.
Y una recomendación que no sale de esta noticia sino del oficio: el repositorio donde vive tu código también necesita copia propia, porque clonar no es tener una copia de seguridad. Un atacante con permiso de escritura en un repositorio puede reescribir historia; si tu única copia es el propio remoto, has perdido el punto de comparación justo cuando lo necesitas.
¿Sabes hoy qué alcanza un plugin instalado en el portátil de tu equipo?
Trabajamos la ciberseguridad por el mismo orden que este post: primero el inventario de lo que puede tocar producción, después los permisos de cada cosa, y solo al final la herramienta. Y cuando lo que hay que gobernar es automatización con IA, montamos antes el interruptor y el registro que el automatismo. No vendemos licencias de ninguno de los productos que salen aquí, así que si la conclusión es que tu configuración ya está bien y lo que falta es un hook de pre-recepción, esa será la respuesta.
Hablar con everyWANLo que no afirmamos
No hemos atacado a ningún agente. Nuestro laboratorio reproduce el comportamiento de git sobre el que se apoya el ataque —que es lo que podíamos verificar por nuestra cuenta y lo que nos parecía más fácil de repetir mal—, no el código de Claude Code, Codex, Copilot ni Gemini CLI; que cada uno de ellos ejecute exactamente esa secuencia lo dice la investigación, no nuestra prueba. Los números de versión corregida (2.1.179 y 0.146.0) y el estado de cada fabricante salen de la publicación de AIR Security y de las respuestas recogidas por la prensa técnica; hemos comprobado en el registro de npm que ambas versiones existen y cuándo se publicaron, pero no que la corrección esté dentro: el registro de cambios de cada producto no la atribuye, así que esa parte la sostienen los investigadores. No hemos verificado con una prueba propia si Bitbucket admite hoy nombres de rama de cuarenta hexadecimales, y por eso lo damos como lo que es: lo que afirman los investigadores. La afirmación de GitHub sobre su propia plataforma es de GitHub, y tampoco la hemos probado. Hasta donde hemos podido comprobar hoy no hay un identificador CVE asignado, así que quien busque por CVE no encontrará nada; eso no quiere decir que no vaya a haberlo. Y no tenemos constancia pública de explotación real: lo que hay es una prueba de concepto de los investigadores y una ventana de tres meses entre el aviso y la publicación.
Nota de fuentes
Todo consultado el 19 de septiembre de 2026. Uno, la investigación: la publicación de AIR Security sobre Plugin4Shell, de donde salen el mecanismo (clonar y hacer checkout del commit fijado sin verificar dónde ha aterrizado), la frase «No user interaction of any kind is required. The result is full compromise of the agent and the host it runs on», el calendario (prueba funcional en mayo de 2026, comunicación a los fabricantes en junio, publicación esta semana), la lista de alojamientos expuestos, la actualización automática de plugins activada por defecto en Claude Code y Codex, y la comprobación de una línea con git rev-parse HEAD. Dos, la cobertura técnica: las piezas de Help Net Security (18 de septiembre) y The Hacker News, de donde tomamos la formulación «check out the pinned commit without verifying the checkout landed there, letting an attacker swap in malicious code while the pin still looks intact», la explicación de OpenAI («can interpret a requested commit SHA as a branch name», «materialize a different commit than the one it pinned»), las versiones corregidas 2.1.179 y 0.146.0, la ausencia de parche en Copilot con el matiz de que los investigadores no obtuvieron respuesta y la retirada de Gemini CLI. Tres, The Register (17 de septiembre), de donde salen la declaración de GitHub sobre nombres de rama parecidos a SHA y la explicación de los investigadores sobre por qué no obtuvieron respuesta de Microsoft. Cuatro, la documentación de GitLab: la página de ramas de la documentación oficial, con la frase «Branch names with 40 hexadecimal characters are prohibited, because they are similar to Git commit hashes.» Cinco, nuestro laboratorio: git 2.43.0, tres escenarios (rama con nombre de 40 hexadecimales apuntando a otro commit; repositorio en el que el commit fijado no existe y la rama trampa es la rama por defecto; y un hook de pre-recepción que rechaza el empuje de ramas con ese patrón). Las salidas que reproducimos en el cuerpo son las de esa sesión, abreviadas donde lo indicamos con corchetes. Seis, el registro de npm: consultado hoy en registry.npmjs.org, de donde salen las fechas de publicación de @anthropic-ai/claude-code 2.1.179 (16 de junio de 2026) y 2.1.278, y de @openai/codex 0.146.0 (29 de julio de 2026) y 0.155.1. Lo que es opinión nuestra, y va dicho como tal: que la respuesta del fabricante ante un aviso coordinado es un dato de compra; que la defensa efectiva hoy está en el alojamiento del repositorio; que el portátil de desarrollo pertenece al plano de control de producción aunque no esté en el inventario; y la lista de lo que haríamos esta semana.
Imagen de portada: fotografía de banco libre de derechos. Los textos y la marca los añadimos nosotros encima.