Hacia las 09:00 UTC del 4 de agosto de 2026, un atacante tomó el control de la cuenta de GitHub de Jared Wray, cuyo nombre de usuario es jaredwray . Con ese acceso, modificó directamente la rama main del repositorio de keyv y generó nuevas versiones maliciosas de varios paquetes de las familias keyv y cacheable .
La primera oleada afectó a 11 paquetes de ambos espacios de nombres. Entre ellos estaban keyv, cacheable-request, cache-manager, @cacheable/utils, flat-cache y file-entry-cache . Investigadores de Aikido Security, StepSecurity —que sigue la campaña bajo el nombre ChainDrop—, Socket y Chainguard confirmaron el compromiso durante las primeras horas .
setup.mjs y Math_Symbol.jsLas versiones contaminadas seguían un patrón común. Cada paquete incorporaba dos archivos nuevos:
setup.mjsMath_Symbol.jsAdemás, los atacantes añadían a package.json la instrucción:
"preinstall": "node setup.mjs"
Esto hacía que el código se ejecutara automáticamente cuando un desarrollador o un sistema de integración continua ejecutaba npm install, antes de que terminara la instalación .
El archivo setup.mjs funcionaba como un dropper: descargaba desde GitHub Releases un binario legítimo del runtime de JavaScript Bun y lo utilizaba para ejecutar la segunda fase, un archivo JavaScript fuertemente ofuscado llamado Math_Symbol.js, de aproximadamente 710 a 728 KB .
Microsoft Threat Intelligence confirmó que el payload pertenece a una variante de Mini Shai-Hulud . Entre los datos que intentaba localizar se encontraban :
El ataque no se limitó a los paquetes controlados inicialmente por el mantenedor comprometido. Después de robar tokens de publicación de npm y tokens PAT de GitHub, el gusano utilizaba esos permisos para publicar versiones alteradas de paquetes pertenecientes a otros mantenedores .
La expansión fue rápida y las cifras cambiaron a medida que los equipos de respuesta analizaban el incidente:
El gusano también saltó entre espacios de nombres. No permaneció dentro de las familias keyv y cacheable, sino que alcanzó paquetes de mantenedores y organizaciones no relacionadas, entre ellas Deliveroo, Ornikar, OneReach, Picsart, Qlik y ServiceTitan .
Las credenciales recopiladas se exfiltraban a un repositorio de GitHub controlado por el atacante. Según los análisis disponibles, el malware podía crear un repositorio específico para la extracción o utilizar uno ya preparado con ese fin .
El payload incluía varios canales redundantes de exfiltración . Esto dificultaba que el cierre de un único repositorio o canal interrumpiera por completo la operación.
Los investigadores de seguridad recomendaron tratar cualquier sistema que hubiera ejecutado una instalación de una versión afectada como potencialmente comprometido. Eliminar los archivos maliciosos no basta, porque las credenciales pudieron haber sido copiadas antes de la detección .
Fija las dependencias en versiones conocidas como limpias o vuelve a una versión anterior al compromiso. También se recomienda utilizar mecanismos de sustitución o sobreescritura de dependencias de npm, Yarn o pnpm —por ejemplo, overrides en package.json— para impedir que una versión contaminada vuelva a instalarse por accidente .
Revisa además los archivos de bloqueo:
package-lock.jsonyarn.lockpnpm-lock.yamlLa revisión debe incluir las dependencias transitivas, no solo los paquetes declarados directamente en el proyecto .
npm installNo confíes en una limpieza superficial de una estación de trabajo, un servidor de compilación o un runner de CI/CD que haya instalado una versión afectada. La recomendación es asumir que todos los secretos disponibles en ese entorno pudieron quedar expuestos .
Revoca los tokens existentes y genera credenciales nuevas, prestando especial atención a:
Un detalle especialmente importante es el orden de las acciones. En algunos casos, el malware configuraba watchers —observadores basados en flujos de trabajo de GitHub— capaces de capturar o volver a exponer un token justo después de su creación .
Por ello, los investigadores aconsejaron localizar y eliminar o desactivar esos mecanismos de vigilancia antes de rotar las credenciales .
Borra las cachés de npm, pnpm y Yarn, así como las cachés de compilación de Docker, tanto en los equipos de los desarrolladores como en los runners de CI/CD .
Después, reconstruye los artefactos desde cero. De ese modo se evita que una dependencia contaminada permanezca en una caché de compilación, en una capa de Docker o en otro producto intermedio .
Revisa la actividad reciente de GitHub en busca de repositorios creados sin autorización, flujos de trabajo sospechosos, commits inesperados y cambios en permisos . También conviene buscar artefactos de persistencia que el gusano pudiera haber dejado en el proyecto, como .claude/settings.json o .vscode/tasks.json .
El ataque de Shai-Hulud del 4 de agosto mostró cómo una sola cuenta de mantenedor puede convertirse en el punto de partida de una infección masiva. Los atacantes no necesitaban acceso directo a cada paquete: bastaba con robar las credenciales de publicación de los entornos infectados y reutilizarlas para contaminar nuevos proyectos.
El uso de un binario legítimo de Bun para ejecutar la carga, la propagación mediante tokens robados y la existencia de canales redundantes de exfiltración hicieron que la campaña fuera más sofisticada que muchos ataques anteriores contra la cadena de suministro de JavaScript .
Para los equipos de ingeniería y seguridad, el incidente refuerza varias prácticas básicas: fijar las dependencias, revisar los scripts preinstall y postinstall, desactivar esos scripts cuando sea posible, vigilar la actividad inusual en GitHub y mantener procedimientos de respuesta específicos para compromisos de la cadena de suministro.