Los agentes de código con IA se han cargado al último revisor humano. La cadena de suministro ahora es cosa tuya.
La generación de código agéntica desplazó la revisión de dependencias del ingeniero a una máquina que no revisa nada. La solución no es huir de un lenguaje o un repositorio. Es enjaular lo que ejecutan tus agentes.
Pregunta a un ingeniero qué paquetes se instalaron en la función que se desplegó la semana pasada y antes obtenías una respuesta. Pregúntalo ahora, cuando el código lo escribió un agente, y lo habitual es un encogimiento de hombros. El agente añadió la dependencia. El gestor de paquetes arrastró las suyas. Nadie leyó nada de eso, porque leerlo nunca formó parte del flujo de trabajo que vendía la herramienta. Ahí está el punto incómodo de la seguridad en la cadena de suministro de los agentes de codificación con IA.
Conviene ser precisos sobre qué significa «lo hizo un agente», porque bajo esa frase se esconden tres mecanismos distintos. La herramienta basada en el modelo decide que hace falta una librería y escribe el import. Tu gestor de paquetes expande ese único nombre en un árbol de dependencias transitivas que tú nunca elegiste. El plugin del editor ejecuta la instalación, y a menudo la batería de pruebas, dentro de tu entorno de desarrollo como efecto colateral de «probar rápido». Una persona que elige una librería al menos echa un vistazo al repositorio, al número de descargas, al último commit. El resolutor de dependencias no mira nada, y el plugin ejecuta lo que ese resolutor produjo. Los paquetes que quedan tres capas más abajo reciben exactamente el escrutinio que les da esa cadena: ninguno.
No todos los agentes se comportan así, y el matiz importa. Muchos siguen deteniéndose a pedir confirmación antes de cada comando de shell, y los fabricantes documentan estos controles de aprobación: el agente de Cursor tiene un modo de ejecución automática que corre comandos en lista blanca sin confirmación, y Claude Code de Anthropic incorpora un modo de aceptación automática y listas de permisos junto a sus confirmaciones por defecto. Lo documentado es que esos modos sin fricción existen y son los que se venden por velocidad. Cuántos equipos los dejan activados en la práctica no lo publica ningún fabricante, así que conviene tratar la idea de que la revisión humana cae a cero como argumento razonado, no como dato medido: se deduce de cómo funcionan esos modos, no de una encuesta.
¿Qué ocurre cuando el paquete que nadie revisó es malicioso?
Ocurre un fallo que ya se ha dado, y el recuento no para de subir. El informe State of the Software Supply Chain 2024 de Sonatype contabilizó más de 512.000 paquetes de código abierto maliciosos en un solo año, un 156 % más que el anterior. Es un recuento, no una probabilidad medida, pero la tendencia no admite duda: cuantos más paquetes hostiles hay en los repositorios, más probable es que cualquier resolución de dependencias toque uno, aunque nadie pueda poner una cifra exacta a esa probabilidad. ¿Dónde se escondería un paquete así? La investigación sobre dependencias de Endor Labs encontró que en torno al 95 % de las dependencias vulnerables son transitivas, el tipo indirecto que ningún desarrollador selecciona a mano. Son dependencias vulnerables, no maliciosas, así que la cifra mide dónde se acumulan los fallos, no dónde aterrizan los ataques. Pero comparten escondite: si los fallos accidentales ya se concentran en las capas que un agente resuelve en silencio, ahí es exactamente donde también se ocultaría código plantado a propósito.
El incidente documentado lo hace tangible. Los investigadores de seguridad de Snyk describieron a un desarrollador que probaba una integración del editor Cursor y que arrastró una copia envenenada de un popular paquete de Python como dependencia transitiva. El código malicioso escribía un archivo de arranque oculto que se ejecutaba al importarse. Lo que lo delató fue casi cómico: la carga maliciosa lanzaba subprocesos de Python de forma recursiva hasta agotar la memoria de la máquina y bloquearla. La detección fue un accidente de agotamiento de recursos, no un control que saltara a tiempo.
Si sigues el mecanismo, la lección incomoda. Nadie tuvo que ser descuidado. El comportamiento ordinario de las herramientas bastó: el agente pidió un paquete, el resolutor añadió algo de lo que ese paquete dependía, la integración lo ejecutó. Ninguna persona eligió el código malicioso. Tres pasos automatizados se ocuparon de todo, de principio a fin, y ninguno audita nada.
Huir del repositorio es el reflejo equivocado
El instinto tras un incidente así es desconfiar del ecosistema entero: culpar al repositorio, refugiarse en un lenguaje con «mejor fama». Eso malinterpreta dónde está la exposición. Nunca estuvo en la marca de un repositorio. Está en código sin auditar varias dependencias por debajo, y eso lo tiene cualquier ecosistema público. Los recuentos de paquetes maliciosos de Sonatype abarcan npm, PyPI y el resto, así que cambiar de repositorio solo cambia de qué marca desconfías mientras el código sin leer sigue en tu servidor de compilación.
¿Cómo se enjaula de verdad a un agente de codificación?
Tres controles hacen casi todo el trabajo, y ninguno es exótico ni hipotético. Los tres apuntan al mismo objetivo: dejar de intentar decidir si un paquete es de fiar y limitar lo que puede hacer uno que no lo sea.
Primero, salida de red denegada por defecto en la compilación. Un agente de build necesita un índice de paquetes y quizá un servidor git. No necesita el endpoint de metadatos de tu nube, tu red interna, ni un servidor arbitrario al otro lado del mundo. En GitHub Actions este control ya está desplegado hoy a través de Harden-Runner de StepSecurity, que bloquea el tráfico saliente por defecto y admite una lista de permitidos:
# .github/workflows: bloquea la salida, permite solo lo que necesita el build
- uses: step-security/harden-runner@v2
with:
egress-policy: block
allowed-endpoints: >
pypi.org:443
files.pythonhosted.org:443
github.com:443
Una carga maliciosa que intente exfiltrar una clave SSH ya no tiene adónde enviarla, y Harden-Runner registra la llamada bloqueada, así que el intento queda en el log de la ejecución. Has limitado lo que el paquete puede hacer sin pronunciarte sobre si es seguro.
Segundo, instalar desde un lockfile versionado con hashes, y rechazar cualquier cosa que no coincida. Fijar la versión de nivel superior no basta, porque el peligro vive en el árbol transitivo. Un lockfile registra cada paquete resuelto y su hash, así que una versión sustituida o envenenada no puede colarse en silencio. Los comandos ya existen: npm ci instala estrictamente desde un package-lock.json versionado, pip install --require-hashes -r requirements.txt rechaza cualquier descarga cuyo hash falte o no coincida, y añadir --ignore-scripts en npm bloquea la ejecución de código en tiempo de instalación en la que se apoyan muchos ataques. Con los hashes fijados, una copia mala deja de ser una descarga silenciosa y se convierte en un diff que una persona, o un escáner, revisa antes de que se ejecute nada:
# package-lock.json, visible en el pull request
"node_modules/log-helper": {
- "version": "2.4.0",
- "integrity": "sha512-6b1e2c..."
+ "version": "2.4.1",
+ "integrity": "sha512-00d4af..."
Sin el lockfile, el resolutor simplemente descarga la nueva versión y el cambio es invisible. Con él, la mutación queda como una línea roja a la espera de revisión en lugar de una instalación silenciosa.
Tercero, escanear automáticamente cada cambio de dependencias y hacer fallar la compilación cuando encuentre algo que no puede justificar. Aquí es donde las herramientas ya han alcanzado el problema. OSV-Scanner compara los lockfiles con una base de datos pública de vulnerabilidades, y servicios como Socket inspeccionan cada paquete añadido en busca de scripts de instalación, código ofuscado y accesos de red inesperados directamente en el pull request. Conecta uno de estos al mismo entorno aislado y ejecútalo en cada cambio del lockfile:
# .github/workflows: falla el PR ante una dependencia conocida o recién añadida sospechosa
- uses: google/osv-scanner-action@v2
with:
scan-args: "--lockfile=package-lock.json"
Añadir un modelo encima es donde creo que la economía empieza a inclinarse por fin hacia quien defiende, aunque esta parte es argumento, no un benchmark: los mismos modelos que instalan sin leer también pueden leer un diff de dependencias más rápido que una persona, señalar un hook de arranque y trazar una ruta de exfiltración. Usado así, el atacante automatiza el ataque y tú automatizas al auditor. Los escáneres anteriores son el núcleo desplegable; el modelo es un acelerador encima, no un sustituto del entorno aislado ni del lockfile.
Aplicamos la misma disciplina en cada encargo de consultoría, y recorre también el resto del análisis que publicamos: mantener a la persona en el circuito donde importa el criterio, y acotar a la máquina donde no.
Los agentes no van a volver a la caja, y no deberían. Pero un equipo que instala lo que nunca lee ha automatizado su propia última línea de defensa hasta hacerla desaparecer. La respuesta no es confiar más en los paquetes. Es dar a la compilación un lugar seguro donde correr, un lockfile que haga visible la manipulación, y un escáner que lea cada cambio para que ninguna dependencia llegue a producción sin haber sido leída.
Preguntas frecuentes
¿Son los agentes de codificación con IA más peligrosos que un desarrollador humano instalando paquetes?
Es el mismo tipo de ataque a la cadena de suministro, pero la exposición es mayor en los modos de auto-aprobación e instalación en segundo plano que muchos equipos activan por velocidad, donde el agente resuelve y ejecuta dependencias transitivas sin que casi nadie les eche un vistazo, así que un paquete comprometido puede propagarse antes de que nadie se dé cuenta.
¿Debería cambiar de lenguaje de programación tras un ataque a la cadena de suministro?
No. Cualquier ecosistema público contiene código transitivo sin auditar, y la investigación de Endor Labs sitúa en torno al 95 % de las dependencias vulnerables en esa capa transitiva, así que cambiar de lenguaje solo cambia de qué marca desconfías, no resuelve nada. Fija las versiones con un lockfile con hashes, verifica la procedencia y aísla la ejecución.
¿Puede un agente de IA ayudar a defenderse de paquetes maliciosos en lugar de ser solo el origen del riesgo?
Sí, y el núcleo desplegable no tiene nada de exótico: ejecuta las compilaciones en un entorno aislado con salida de red denegada por defecto, como Harden-Runner, y pon un escáner automático como OSV-Scanner o Socket en cada cambio de dependencias para detectar scripts de instalación, ofuscación y llamadas de red inesperadas. Un modelo puede triar esos hallazgos más rápido que una persona, pero trátalo como un acelerador sobre el escáner y el entorno aislado, no como su sustituto.
Relacionado
- En Ubuntu 26.04 LTS, el coreutils del que depende tu build ya no es de GNU
- La prima de la soberanía: por qué las soluciones de IA soberana para empresas ganan por acceso, no por velocidad
- Los pleitos por secretos comerciales se ganan años antes de que nadie dimita. Que se lo pregunten a Faccenda Chicken.
- Security & Trust
Escrito por una persona editorial de IA del sistema editorial propietario de Abyshire y revisado por nuestro equipo.