Cuarenta caracteres hexadecimales eran la última promesa en pie: ancla el complemento a este commit y no puede cambiar debajo de ti. Los cuatro agentes de código más usados anclan así cada complemento. Ninguno comprobaba el resultado.

El commit sigue ahí. Sigue siendo el correcto. Sigue siendo el que revisaste.

Y no es el que se ejecutó.


Cuarenta caracteres de garantía

No se ancla una etiqueta, que se mueve; ni una rama, que se reescribe. Se ancla un commit: cuarenta caracteres que resumen criptográficamente el contenido. Si el contenido cambia, cambia el número.

GitHub lo escribió como promesa en la referencia de complementos de Copilot CLI, aún publicada: "sha must be a full 40-character commit SHA. Pin to a sha for reproducible installs that are immune to force-pushes or tag/branch moves." Inmunes. La palabra es suya. La documentación de Claude Code promete menos y lo mismo: el sha es el anclaje efectivo y el agente hace checkout de ese commit directamente.

El 17 de septiembre de 2026, Air Security publicó que los cuatro agentes hacen el git checkout del commit anclado y ninguno comprueba dónde quedó HEAD después. Lo llamó Plugin4Shell: "a zero-click, high-severity RCE affecting all four major AI coding agents - Claude Code, Codex, Copilot, and Gemini."


Git nunca prometió eso

La raíz no está en ninguno de los cuatro programas, sino en una regla de git documentada desde siempre, que AIR resume en una línea: "When a name is both a valid ref and an object id, git prefers the ref". Gana la referencia, y el objeto ni siquiera figura en la lista de desambiguación.

¿Y qué hace git al detectar la ambigüedad? Avisa. Una advertencia por consola, en un proceso de fondo que nadie mira, y sigue. Nunca importa si el commit anclado todavía existe.

Falta una pieza: que alguien pueda llamar a una rama como se llama un commit. El formato de referencias de git no lo prohíbe. La prohibición existe, pero es de una sola empresa: GitHub rechaza los nombres de cuarenta caracteres hexadecimales. Los demás anfitriones que siguen el protocolo los aceptan.


Cinco pasos, ningún clic

La cadena que describe AIR no exige ingenio. Se publica un complemento inofensivo, anclado a aaa...aaa, y pasa la revisión. La gente lo instala. Llega una actualización rutinaria y el catálogo vuelve a anclar, ahora a bbb...bbb, aún limpio. Y entonces el dueño del repositorio crea una rama llamada bbb...bbb, la pone por defecto y la apunta a código malicioso.

El resto lo hace el agente solo: el anclaje nuevo dispara la actualización automática de fondo, el checkout resuelve bbb...bbb a la rama, y el código corre. Sin pantalla, sin permiso, sin nadie despierto. Y al final, la línea más cara del informe: "The agent reports a successful install at the pinned commit."

En Gemini CLI está a la vista, en el código de Google: cloneFromGit, en extensions/github.ts, clona, pide el ref al remoto y ejecuta await git.checkout('FETCH_HEAD');. El comentario de encima lo firma: "This results in a detached HEAD state, which is fine for this purpose." Después no hay una línea que compruebe nada.

Ahí está la parte que duele. Ciento ochenta líneas más abajo, en el mismo archivo, ya vive const localHash = await git.revparse(['HEAD']); — la primitiva exacta del arreglo, usada para saber si hay actualización disponible. Nunca para saber si se instaló lo que se pidió. La herramienta estaba en la caja. Servía para medir, no para verificar.


Un fallo, y lo cometieron los cuatro

"One flaw, and every major lab made it", escribe AIR. Cuatro equipos construyeron la misma garantía y ninguno la comprobó, porque comprobarla se siente redundante: acabas de pedir el commit por su nombre, ¿qué más hay que verificar?

Ese reflejo es el agujero entero, y no se tapa desde el catálogo: "A marketplace cannot fully close this. The pin is resolved inside the agent". El que queda PAILA no es el que instaló sin mirar. Es el que ancló, revisó y confió en el número.

Con ClawHavoc contamos un catálogo con 341 skills maliciosos de 2.857, sin revisión ni firma. Plugin4Shell es peor: aquí sí hubo revisión y sí hubo anclaje, y eran decorativos. Tampoco es cosa de laboratorio: casi el noventa por ciento del Fortune 500 usa Copilot, según Microsoft.


El arreglo lleva sesenta y seis días escrito

El 16 de julio de 2026, dos meses antes de que nadie publicara nada, DavidAPierce abrió el pull request #28422 en gemini-cli: describe la variante de Gemini —si la rama por defecto se llama FETCH_HEAD, el checkout resuelve a la rama— y añade lo que faltaba, comprobar con git rev-parse HEAD que el commit que quedó es el que se pidió. bdmorgan lo aprobó nueve minutos después. Hardanish-Singh lo aprobó el 12 de agosto, y esa aprobación es la última actividad del hilo: al 20 de septiembre sigue abierto, sin fusionar y con conflictos. ¿Cuántas aprobaciones hacen falta?

Tampoco es un repositorio abandonado: la API de GitHub responde archived=false y 107.090 estrellas, con commits el 17 y el 18 de septiembre. El producto está descontinuado; el repositorio está vivo; el arreglo está escrito y aprobado; el archivo vulnerable no se toca desde el 13 de agosto.

El 4 de agosto Google le confirmó a AIR que no habrá arreglo, por estar Gemini CLI descontinuado. Dos meses y medio antes, su anuncio de transición había prometido que el producto seguiría accesible con llaves de pago, y algo más: "We'll continue to support Gemini CLI and Gemini Code Assist with access to the latest Gemini models and other updates." Un RCE sin parche, por lo visto, no entra en "other updates".

Los dos que sí arreglaron no lo llamaron seguridad. Claude Code 2.1.179 salió en npm el 16 de junio y Anthropic confirmó el arreglo a AIR el 17. La entrada de esa versión en el CHANGELOG tiene nueve viñetas, y la única que nombra los complementos dice "Improved plugin loading performance in remote sessions". Rendimiento de carga. Ninguna menciona el arreglo.

En Codex el parche sí es público: el pull request #34644, Verify Git plugin SHA checkouts, con el fallo escrito sin rodeos —"Git can interpret a requested commit SHA as a branch name when the remote's default branch has the same name"— y una prueba de regresión. Lo abrió copyberry[bot] y lo fusionó copyberry[bot], ciento treinta y cinco segundos después: no es urgencia, es un espejo automatizado. En las notas de la versión 0.146.0 figura una sola vez, en la lista cruda de pull requests: ni en New Features ni en Bug Fixes, y esa línea no lleva la palabra seguridad.

Anthropic no lo escribió en ninguna parte. OpenAI lo escribió en una línea, la 34644 de una lista de cientos.

El ecosistema respondió a la altura: once puntos y tres comentarios en Hacker News. Uno se quejó de la prosa. Otro remató: "Pretty bad for a package manager, but this seems like something I would unfortunately expect from a harness/agent." Lo esperaba.


Atribución

Perpetrador: los cuatro agentes. Cada uno pidió un commit por su nombre y aceptó lo que le devolvieron. El arreglo que propone AIR cabe en una línea: test "$(git rev-parse HEAD)" = "<pinned-sha>" || abort. Dos ya la tienen. Copilot y Gemini CLI, al 20 de septiembre, no. Microsoft ni siquiera respondió a la divulgación; AIR lo atribuye al volumen de reportes que recibe.

Cómplices: la documentación. Las dos promesas que abren este artículo siguen publicadas, sin advertencia y sin una línea que diga que el agente comprueba el commit resultante. GitHub tiene un argumento legítimo y lo dio —"This mitigation ensures the reported vulnerability cannot be exploited on GitHub", declaró un portavoz a The Register—, cierto e insuficiente: su propia documentación admite que un catálogo de Copilot puede vivir en cualquier otro servicio de git. La protección no la pone el agente. La pone un anfitrión, y solo uno. El mismo patrón seguía vivo en otra ruta de Claude Code, y el reporte que lo documentaba lo cerró un robot por inactividad.

Falla sistémica: anclar a un SHA es un ritual, igual que revisar. Se hace una vez, se siente definitivo, y nadie vuelve a mirarlo. Ya lo escribimos con el confused deputy: el permiso se concede antes y nadie lo revisa después. Aquí el engaño ni siquiera entra por la conversación: entra por el instalador. El contrapunto va entero: quien publica la alarma vende la alarma. AIR se presentó en público el 1 de septiembre, dieciséis días antes, con un producto que vende como cortafuegos del contexto en el que operan los agentes; y su informe incluye la línea "Enterprises using Air Marketplace and Air Filter were not affected by Plugin4Shell." Pebblous lo dice sin acusar a nadie: la gravedad y el alcance los pone quien vende el remedio, y los millones de agentes del titular son estimación propia de AIR, sin verificación de terceros.


Al 20 de septiembre no hay CVE, no hay GHSA y ningún fabricante publicó un aviso. The Hacker News lo verificó el 18 y añadió la frase que lo explica: "there is no sign it has been used in a real attack." Cero víctimas conocidas. Por eso nadie se asustó.

El commit que revisaste sigue en su repositorio, intacto, con su hash correcto. Nunca se movió. ¿Cuántas de tus certezas son así — verdaderas, verificables, y sobre la cosa equivocada?