La documentación nombraba un paquete que no era de nadie
Unos investigadores escanearon 6.214 dominios corporativos en busca de llms.txt, el archivo legible por máquina que las empresas publican hoy para los agentes de IA, y encontraron 237 paquetes y dominios nombrados en esos archivos que nadie había registrado jamás. Reclamaron unos cuantos. El primer agente de programación dentro de una red Fortune 500 instaló uno en menos de cuatro minutos. El atacante no tocó ningún mantenedor, ningún registro ni ningún repositorio. La barrera de edad de Bromure Agentic Coding viene activada de fábrica en dos días, y un nombre que un atacante reclama es, por construcción, una publicación recién salida.
Un proveedor escribió en su propia documentación un comando de instalación para un paquete que nunca publicó. El nombre quedó ahí, libre, a disposición de cualquiera. Los agentes de programación leyeron la documentación, la creyeron y ejecutaron la instalación.
En algún momento de los dos últimos años, sus herramientas de desarrollo
empezaron a distribuir un archivo nuevo. Está junto a robots.txt, se llama
llms.txt y contiene un resumen en texto plano del producto, escrito para una
máquina y no para una persona. Qué hace la API. Qué endpoints importan. Qué
paquete instalar para empezar. Los agentes de programación lo descargan y, como
viene del dominio del propio proveedor, lo tratan como un hecho consumado.
Un investigador llamado Alon Hertz, que trabaja bajo el nombre PANDEX, fue a ver
qué le dicen esos archivos a un agente que haga. Su método tiene tres pasos y
ningún exploit: bajar llms.txt y llms-full.txt de los directorios de
empresas, lanzar agentes de verdad contra ellos en entornos aislados y mirar a
qué se lanzan los agentes, y después comprobar cada nombre de paquete y cada
nombre de host que salió de ahí contra npm, PyPI y los registros de dominios.
Describe las herramientas en whatwouldai.do, bajo un
lema que resultó ser el hallazgo: los agentes actuarán sobre cualquier cosa.
El escaneo cubrió 6.214 dominios activos, tomados de empresas de la Fortune 500, grandes tecnológicas, fintech y contratistas de defensa, y más de 8.000 de estos archivos. En 120 de ellos, lo que el archivo le mandaba instalar al agente no existía. En todo el corpus, 237 nombres de paquetes y de hosts nombrados estaban libres: disponibles en npm, PyPI, RubyGems, NuGet, crates.io y Packagist, o libres como subdominios en Vercel, Render, Fly y Netlify. Cualquiera podía tomarlos por el precio de un registro.
Así que los investigadores tomaron un puñado, publicaron paquetes inofensivos bajo esos nombres con una baliza que llamaba a casa al instalarse, y esperaron.
La primera llamada llegó en menos de cuatro minutos. La segunda, dentro de la hora. A lo largo del periodo siguiente registraron instalaciones desde decenas de organizaciones, varias de ellas entornos Fortune 500, provocadas por agentes que corrían sobre Claude de Anthropic, Codex de OpenAI y Hermes de Nous Research. Bruce Schneier recogió el estudio el 4 de septiembre y lo leyó como merece: la línea entre datos y código ejecutable se está desmoronando. Un investigador citado en esa entrada lo dice con menos palabras. El modelo de confianza está roto. Los agentes tratan la documentación del proveedor como verdad de campo y no la cuestionan.
Clerk documentó un paquete que nunca entregó
El ejemplo más claro viene de un proveedor de autenticación, cuya documentación le dice al lector que ejecute:
npx clerk-next-fix-auth-protection
Clerk nunca publicó un paquete independiente con ese nombre. Quizá el comando era
una abreviatura, quizá apuntaba a algo interno, quizá un redactor se equivocó. El
motivo da igual, porque para un agente el nombre se leía como real. Otra persona
lo registró. Las bases de avisos de seguridad recogen ahora ese paquete como
MAL-2026-11069, e hizo lo que se construye para reconocimiento: los hooks de
instalación de npm se dispararon y enviaron fuera el nombre de la cuenta que
instalaba, el nombre de host de la máquina, el directorio de trabajo y una marca
de tiempo.
Eso es un mapa del entorno de compilación de alguien, recogido dentro de su red por su propio agente de programación, que estaba siguiendo la documentación de su propio proveedor.
Por qué el consejo de siempre apunta justo a la carga
Toda guía de cadena de suministro le dice que compruebe el nombre de un paquete contra la documentación oficial. Ese consejo falla aquí, porque la documentación oficial es exactamente de donde salió el nombre malo.
Invierta el problema del que escribimos en julio y obtiene este. Allí un agente alucina un nombre de paquete plausible y un atacante registra los nombres que suele inventarse, así que el paso débil es la conjetura del modelo. Aquí el modelo no conjetura nada. Va al proveedor, lee lo que el proveedor dice, lo sigue, y es esa secuencia cuidadosa la que lo atrapa.
Un compromiso de registro también tiene otra forma. Cuando un flujo de release publicó diez versiones envenenadas de un paquete real el mes pasado, algo tenía que romperse primero: un flujo que confiaba en un comentario. Aquí no se rompe nada. Un redactor técnico teclea un comando que no encaja del todo con un artefacto publicado, que es el error más corriente del software, y el hueco entre el nombre y el artefacto se convierte en una primitiva de ejecución que cualquiera en internet puede recoger.
La capa que atrapa esto ya está encendida
Bromure Agentic Coding ejecuta cada agente de programación en una VM Linux virtualizada por hardware en su Mac, y cada paquete que el agente descarga pasa por un proxy en el lado anfitrión de esa frontera, antes de que un solo byte llegue a la VM. Allí se aplican varias capas. Para esta historia, una de ellas hace el trabajo por sí sola, y es justo la que un espacio de trabajo nuevo ya trae encendida.
La barrera de edad convierte un nombre recién reclamado en un no-evento. Un
espacio de trabajo rechaza versiones de paquete más jóvenes que un corte, fijado
en dos días de fábrica. El proxy lo impone reescribiendo el listado de versiones
del registro al pasar: elimina las versiones más jóvenes que el corte y vuelve a
apuntar latest a la superviviente más reciente. Un paquete publicado por
primera vez esta mañana no tiene ninguna versión lo bastante vieja para
sobrevivir a ese filtro, así que desde el punto de vista del agente no hay nada
que instalar en ese nombre. Si el agente pide directamente una versión demasiado
fresca, la descarga vuelve como un HTTP 451 cuyo cuerpo indica la edad real del
paquete y el mínimo exigido, y npm imprime ese texto tal cual.
Lea el ataque otra vez con eso puesto. Toda la técnica depende de que un nombre pase de no publicado a publicado en el momento que elija el atacante. Esa transición es una publicación recién salida. La única propiedad que el atacante no puede evitar es aquella sobre la que se apoya la política por defecto.
El recorte de scripts de instalación elimina exactamente el mecanismo que usó
la carga. El paquete observado hacía su trabajo en los hooks preinstall,
install, postinstall y prepare de npm. Con el recorte de scripts activado,
el proxy descomprime cada tarball de npm al vuelo, borra esas cuatro claves del
package.json, limpia dist.integrity y dist.shasum de los metadatos para que
la propia comprobación de hash de npm siga pasando, y vuelve a comprimir. El
paquete se instala; la baliza no tiene desde dónde correr. Los paquetes que
necesitan sus hooks, como los compiladores de bindings nativos tipo
better-sqlite3, entran en una lista de permitidos por espacio de trabajo y los
conservan.
El filtrado por reputación conoce este paquete por su nombre.
MAL-2026-11069 es un aviso de malware. Con socket.dev elegido como filtro de
paquetes del espacio de trabajo, su bloqueo de paquetes comprometidos se dispara
ante señales de malware y de typosquat en cualquier severidad; la comprobación
gratuita de OSV cubre los ocho ecosistemas interceptados sin clave de API. Cuando
una fuente de reputación no puede emitir un veredicto, ya sea por limitación de
tasa, por una red caída o por un ecosistema que no cubre, Bromure falla en
cerrado en lugar de permitir la descarga. La descarga se pausa y le pregunta a
usted, paquete a paquete y versión a versión, de modo que un atacante capaz de
provocar un fallo de consulta no gana nada.
El reconocimiento que recoge pertenece a la VM. Todos los titulares sobre este estudio dicen lo mismo: los agentes están instalando código desconocido en redes corporativas. La cosecha de la baliza fue el nombre de usuario que instalaba, el nombre de host de la máquina y el directorio de trabajo. En un espacio de trabajo de Bromure esos tres campos describen un invitado Linux en su Mac, y no un host de compilación unido al dominio con un nombre interno que merezca entrar en una lista de objetivos. Las credenciales que hay en esa VM son señuelos cuyos valores reales solo existen en la memoria del proxy anfitrión. Si un código hostil intenta llevar una de ellas a un host para el que no fue acuñada, el proxy bloquea la petición y pausa la VM en pleno vuelo.
La denegación por defecto responde a la otra mitad del estudio. Buena parte de los nombres sin reclamar eran nombres de host y no paquetes: dominios caducados y subdominios gratuitos en Vercel, Render, Fly y Netlify, sentados en documentación que los agentes descargan. El cortafuegos de salida del espacio de trabajo es una tabla ordenada de reglas con un ajuste Unmatched traffic; póngalo en Deny y la VM abre conexiones hacia los hosts que usted haya listado y hacia nada más, en cualquier protocolo. Dos componentes lo imponen fuera del invitado: el conmutador de red virtual casa cada flujo por la dirección IP de destino, y el proxy anfitrión vuelve a casar por el nombre de servidor TLS. Una baliza apuntada a un dominio que alguien registró el martes pasado no sale de la máquina. Las ediciones de reglas alcanzan a las sesiones en marcha sin reiniciar.
Cada intento se lleva una fila. En el nivel de traza Activity only, el
proxy escribe un registro de metadatos por cada petición que sale de la VM, con
marca de tiempo, host, puerto, método, ruta, estado, latencia y bytes, y no
almacena ningún cuerpo de petición. bromure-cli trace hostnames my-workspace
imprime cada host distinto que el espacio de trabajo contactó, con sus recuentos.
Usted ve un dominio de retorno que ninguno de sus ingenieros reconoce el mismo
día en que ocurre, en su propia máquina, en lugar de leerlo meses después en el
conjunto de datos de otro.
Lo que ya está encendido
Un espacio de trabajo nuevo viene con la barrera de edad habilitada en un
mínimo de dos días, y esa es la configuración con la que choca este ataque.
Súbala si su trabajo tolera la espera, hasta 90 días en el selector, y use
Exempt packages para los que su propio equipo publica e instala en la
misma hora, con la forma npm:nuestro-paquete.
Lo que vale la pena encender hoy
En el panel Supply Chain del espacio de trabajo, habilite el recorte de scripts de instalación y elija un filtro de paquetes. En Guardrails, ponga Unmatched traffic en Deny y liste los hosts que el trabajo necesita. En Tracing, Activity only guarda el rastro de hosts contactados y nada del contenido. Cada una de esas ediciones se aplica a sesiones que ya están corriendo.
Nadie corrige un archivo escrito para máquinas
El escáner de PANDEX caza una cosa más. Junto a los paquetes y dominios sin
reclamar, busca instrucciones dirigidas al agente en vez de al lector: directivas
plantadas en el archivo, a veces escondidas con trucos de Unicode y caracteres de
ancho cero. La tubería de compilación de un proveedor genera llms.txt, su
agente lo consume, y nadie en ninguna de las dos empresas lee lo que pasó entre
ellas.
Bromure trata una página descargada como entrada no confiable. El contenido que
el agente trae de fuera, ya sea contenido de archivos, páginas web, cuerpos de
incidencias o salida de comandos, llega al modelo como un span tool_result. Con
el detector de código fuente habilitado, un modelo PromptGuard que corre en su
Mac puntúa esos spans en el cable antes de que el modelo actúe sobre ellos, y
usted elige si registra, pregunta o bloquea. Ningún contenido va a ninguna parte
para ser analizado.
Hemos pasado una década aprendiendo a verificar artefactos: firmar el paquete, fijar el hash, atestiguar la compilación. Este estudio va del paso anterior a todo eso, aquel en el que un agente decide qué artefacto pedir, trabajando a partir de un documento que no lleva firma alguna y que escribió un redactor técnico con prisa. La documentación se ha convertido en una entrada ejecutable, y los registros están llenos de nombres libres esperando a que alguien se dé cuenta de que un proveedor los mencionó.
Pásele un grep a su propio llms.txt buscando nombres que nunca publicó. Es el
trabajo de una mañana y lo saca de la lista de objetivos de alguien. Después
dele a su agente una máquina donde un paquete publicado hace cuatro minutos no
sea instalable, y donde usted pueda ver todos los sitios adonde fue.
Instale Bromure Agentic Coding y ponga la ruta de instalación
en el anfitrión.