El repositorio sí era de Microsoft
El 5 y 6 de junio de 2026, el gusano Miasma metió código robacredenciales en 73 repositorios de cuatro de las propias organizaciones de GitHub de Microsoft — Azure, Azure-Samples, microsoft, MicrosoftDocs — incluido Azure/functions-action, la Action de despliegue oficial, y durabletask, un repo que ya había sido limpiado una vez en mayo. Esta vez la carga útil no esperó al npm install. Se disparó en el momento en que un desarrollador abrió el repositorio en Claude Code, Cursor, Gemini CLI o VS Code. Aquí está por qué la señal de confianza — «es un repo de Microsoft» — fue de nuevo la superficie de ataque, y qué cambia cuando el agente que lo abre vive en una VM Bromure por perfil, detrás de un intermediario de credenciales, un guardrail de lectura/escritura y un período de espera para paquetes.
Hace una semana el gusano se coló por el ámbito npm de Red Hat y se
disparó a través de un hook preinstall. Esta semana se coló por el
GitHub de Microsoft y no necesitó ninguna instalación en absoluto.
Miasma plantó configuración con ámbito de proyecto en 73 repositorios
propiedad de Microsoft — Azure/functions-action, la Action de
despliegue oficial, entre ellos — y la carga útil se ejecutó en el
momento en que un desarrollador abrió el repo en Claude Code, Cursor,
Gemini CLI o VS Code. Clonar no era el disparador. Abrirlo en tu
agente sí.
Un desarrollador que clona Azure/functions-action para depurar un
despliegue que falla no duda, y tampoco lo hace el agente al que lo
apunta. Es un repositorio de primera mano de la propia organización
Azure de Microsoft — la fuente canónica de la GitHub Action que media
el ecosistema referencia como Azure/functions-action@v1. No hay
mantenedor que examinar, porque el mantenedor es Microsoft. No hay
ningún nombre que escudriñar, porque el nombre es exactamente lo que
debería ser. Así que el repo se abre, y el agente lee la configuración
del proyecto como hace toda herramienta de codificación moderna al
abrir la carpeta — y uno de esos archivos de config apunta a un
comando, y el comando es un blob de aproximadamente 4,3 megabytes
que empieza a leer el sistema de archivos en busca de llaves.
Documentamos la mitad npm de esta misma campaña hace siete días. Miasma es el mismo gusano — una variante del código «Mini Shai-Hulud» que TeamPCP publicó abiertamente a mediados de mayo — y el hecho incómodo que demuestra es el mismo, girado una vuelta más: fuente reputada no es un control de seguridad. Parece serlo. La mayoría de los consejos sobre cadena de suministro se apoyan en ello. Y el 5 de junio no compró absolutamente nada, dos veces: el namespace era de Microsoft, y la ejecución no vino de un paquete que elegiste instalar. Vino de abrir una carpeta.
Qué le hizo Miasma a los repositorios de Microsoft.
El 5 y 6 de junio de 2026, GitHub deshabilitó 73 repositorios de cuatro organizaciones de GitHub de Microsoft después de que se empujaran commits maliciosos a ellos, según The Hacker News y un desmontaje detallado de StepSecurity. Redmond Magazine lo cubrió el 8 de junio. El desglose:
- Azure — 49 repositorios, incluidos
Azure/functions-action(la Action de despliegue oficial de Functions) y los language workers para .NET, Python, Java, Go y PowerShell. - microsoft — 10 repositorios.
- Azure-Samples — 13 repositorios.
- MicrosoftDocs — 1 repositorio.
La forma, reducida a su mecánica:
- El punto de entrada fue una cuenta de contribuidor previamente
comprometida con acceso de commit, usada para empujar commits
maliciosos directamente a los repositorios — etiquetados
[skip ci]para que los cambios pasaran de largo los controles de CI/CD que de otro modo se habrían ejecutado. - El commit plantó configuración con ámbito de proyecto — el tipo de archivo que un agente de codificación o un IDE lee y sobre el que actúa automáticamente cuando abres la carpeta: una task de editor, un hook de agente, un servidor MCP definido en el proyecto. Esta es la misma clase de frontera de confianza que TrustFall de Adversa AI demostró en Claude Code, Cursor CLI, Gemini CLI y Copilot CLI — los cuatro ejecutan configuración definida en el proyecto justo después del aviso de confianza de la carpeta.
- La carga útil — aproximadamente 4,3 MB de código ofuscado — se
ejecutó cuando el repositorio se abrió en Claude Code, Gemini CLI,
Cursor o VS Code, o se corrió a través de un script
npm test. No solo al clonar. El acto de apuntar tu agente al árbol clonado es lo que lo ejecutó. - Al ejecutarse, barría el host en busca de tokens de GitHub, llaves
de AWS, service principals de Azure, credenciales de GCP, tokens de
publicación de npm y PyPI, llaves SSH y archivos
.env, luego usaba el acceso robado para hacer commit de sí mismo más adelante — que es lo que lo convierte en un gusano y no en un disparo único.
Un detalle merece detenerse: Azure/durabletask estaba entre los
repositorios afectados — y ya había sido comprometido en mayo en la
campaña de TeamPCP y limpiado. Un repo que fue remediado una vez fue
reenvenenado cinco semanas después. La limpieza no es un estado que
alcanzas y conservas; es un estado del que te caes en el momento en que
otra credencial de la cadena es robada.
Vale la pena ser igual de preciso sobre lo que no pasó. La red
corporativa de Microsoft no fue vulnerada. Azure, el servicio en la
nube, no fue vulnerado. No se tocó ningún dato de cliente ni ningún
sistema de producción. Este fue un ataque a repositorios de código
fuente — y su consecuencia de mayor alcance no tuvo nada que ver con
el malware en absoluto: en el instante en que GitHub deshabilitó
Azure/functions-action, todo pipeline del planeta que referenciaba
Azure/functions-action@v1 dejó de resolver. Microsoft fue el
portador de más alto perfil. La gente que de verdad fue comprometida
fueron los desarrolladores que abrieron los repos envenenados en un
agente entre el 3 y el 5 de junio, y que vieron sus credenciales
barridas de sus propias máquinas.
El mismo repo, abierto dentro de Bromure Agentic Coding.
Bromure Agentic Coding ejecuta tu agente de
codificación dentro de una VM Linux por perfil — su propio kernel,
su propio sistema de archivos, su propia pila de red, sobre el
framework de Virtualización de Apple. Un perfil es un ámbito de trabajo
coherente: este cliente, este servicio interno, este repo
open-source que clonaste para depurar un despliegue. Clonas
Azure/functions-action en ese perfil y lo abres con tu agente ahí
dentro. El disparador de abrir la carpeta se dispara exactamente como
fue diseñado. La carga útil corre. Y entonces se pone a buscar llaves
en un host que no puede alcanzar.
Porque las credenciales no están en el perfil. La VM viene con
stubs — tokens falsos que parecen reales para git, gh, aws,
kubectl, npm y cualquier otra cosa que espere un encabezado
Authorization. Un proxy en tu Mac se sienta delante de cada conexión
que sale del sandbox, reconoce el stub, y lo intercambia por el secreto
real en el cable a medida que la petición sale
(el sandbox que guardaba la llave
recorre el mecanismo). El PAT real de GitHub, la llave real de AWS, el
principal real de Azure — ninguno de ellos toca un archivo, una
variable de entorno ni una página de memoria que la VM pueda leer. Las
llaves SSH nunca salen del Keychain de macOS en absoluto; solo se
reenvía hacia dentro el socket de ssh-agent, tal como OpenSSH siempre
pretendió.
Así que recorramos el barrido de Miasma a través de esa frontera. La
carga útil lee el entorno en busca de $GITHUB_TOKEN y encuentra un
stub. Lee ~/.aws y no encuentra nada. Lee ~/.npmrc y no encuentra
ningún token de publicación. Lee ~/.ssh y no encuentra ningún archivo
de llave — hay un socket reenviado, no una clave privada en disco. El
blob de 4,3 MB corre hasta completarse exactamente como fue escrito.
Solo que exfiltra una caja que nunca tuvo tus llaves, en el lado
equivocado de una frontera reforzada por hardware respecto a todo lo
que importa.
El paso de propagación es una escritura, y las escrituras reciben un aviso.
Robar llaves es solo la mitad de Miasma. La otra mitad es la
propagación: usó el acceso robado a GitHub para hacer commit de sí
mismo en el siguiente repositorio, y eso es lo que convirtió un puñado
de repos envenenados en 73. Incluso en un perfil que legítimamente
tiene acceso de push — digamos que clonaste functions-action
precisamente porque pretendes abrir un PR contra él — el paso de
propagación del gusano todavía tiene que salir a través del proxy, y
ahí es donde los Guardrails se topan con él.
Los Guardrails leen la operación, no solo la conexión — distinguen
una lectura de una escritura. Un git fetch es una lectura; un git push es una escritura. Pon la credencial de GitHub de un perfil en
preguntar al escribir, y en el momento en que el agente echa mano
de una llamada que cambia estado — el git-receive-pack que el gusano
necesita para hacer commit de su config de vuelta, un DELETE contra
una API, un Terminate* en EC2 — Bromure lo detiene en el cable y hace
aparecer un aviso en tu Mac que nombra el verbo, el destino y el
perfil. La concesión que das está acotada en el tiempo: quince minutos
para una publicación, de un solo uso para las que dan miedo, nunca si
la petición no tiene sentido. Las lecturas nunca te interrumpen; el
agente hace fetch y grep y lee todo el día. Es la mutación lo que se
pausa.
Esta es la diferencia entre «el agente tiene un token» y «el agente puede hacer lo que quiera con el token». Todo el mecanismo de propagación de Miasma es una escritura que el agente nunca te dijo que estaba haciendo — y una escritura que el agente nunca te dijo que estaba haciendo es exactamente lo que el aviso de lectura/escritura está construido para atrapar. El push que propaga el gusano se convierte en un cuadro de diálogo en el que haces clic en No permitir, del mismo modo en que «el agente borró la base de datos de producción» deja de ser un postmortem y se convierte en un aviso que rechazaste.
La versión tenía horas, y Bromure hace que los paquetes envejezcan.
Hay una segunda manera en que Miasma — y el linaje Mini Shai-Hulud más
amplio — alcanza a un desarrollador: no a través de un repo que abres,
sino a través de un paquete recién envenenado que el agente instala
mientras hace su trabajo. La mitad de Red Hat de esta campaña fue
precisamente eso, un hook preinstall en 32 paquetes de un ámbito de
confianza. Y el detalle brutal de esos incidentes es el momento: una
versión comprometida típicamente se detecta y se retira en cuestión de
horas — pero esas son exactamente las horas durante las cuales un
agente autónomo, corriendo sin supervisión, podría tirar de ella.
La capa de Cadena de suministro de Bromure convierte el mismo proxy de frontera en un punto de control de escaneo, y hace las dos cosas que de verdad importan contra un compromiso del mismo día:
- Fuerza el escaneo de cada descarga contra socket.dev además de OSV. OSV atrapa los CVE conocidos por encima del umbral de severidad que fijes. socket.dev atrapa lo que las bases de datos de vulnerabilidades aún no han alcanzado — scripts de install maliciosos, malware conductual, typosquats, el compromiso recién publicado. Una versión marcada se bloquea antes de que el tarball llegue siquiera a la VM. Crucialmente el escaneo corre por debajo del agente, en el proxy: por mucho que el agente reescriba su propia config para rodearte, la descarga sigue saliendo por la frontera que no puede cruzar.
- Impone un período de espera. Bromure pone en cuarentena cualquier
versión publicada en los últimos dos días — ajustable — de modo que
una versión subida hace una hora sencillamente no es instalable en
ese perfil mientras el ecosistema se pone al día. Contra un gusano
cuya ventana entera de oportunidad es el hueco entre publicar y
retirar, un período de espera no es una heurística sobre si un
paquete parece malo. Es una negativa a ser el primero en
averiguarlo. Combínalo con la eliminación de scripts de install que
Bromure hace al vuelo — sacando los hooks
postinstalldel tarball y arreglando el hash de los metadatos para que la instalación todavía verifique — y el paquete que sí llega, llega inerte.
Para Miasma en concreto, el vector de abrir-el-repo es el titular. Pero
la misma campaña se propaga también a través de paquetes, y el período
de espera es el control que habría matado de hambre el lado npm de
ella: una versión reciente de @redhat-cloud-services, o una
dependencia transitiva recién envenenada tirada mientras depuras ese
repo de Microsoft, se queda en cuarentena durante las horas exactas en
que es peligrosa.
Donde esto no te salva.
Un push que apruebas es un push que ocurre.
El guardrail de lectura/escritura atrapa la escritura de la que el
agente no te avisó. No lee el diff. Si legítimamente estás
empujando a functions-action y apruebas el aviso, Bromure reenvía
el push — incluyendo, en principio, un workflow envenenado que no
notaste en el diff. Lee lo que apruebas. El trazo de
sesión te dice qué diffs
leer.
El período de espera es una ventana, no un muro.
Dos días está ajustado al hueco observado entre publicar y retirar, pero un atacante paciente puede sentarse sobre una versión comprometida más tiempo que el período de espera y seguir siendo instalable al tercer día. El período de espera mata de hambre a los gusanos del mismo día; no avala un paquete que sencillamente ha envejecido. socket.dev y OSV todavía tienen que cumplir su parte.
El perfil es de larga duración, así que la persistencia persiste.
Un perfil Bromure no es un disco desechable. Una carga útil que se escribe a sí misma en una ruta de arranque dentro del perfil puede sobrevivir hasta la siguiente sesión en ese perfil. Con lo que se despierta es un invitado sin llaves del host y un intermediario que solo habla en tokens de corta duración, con aviso y ámbito limitado — presencia en una caja sin nada dentro — pero presencia al fin y al cabo.
Dale ámbito al intermediario a propósito.
Si un perfil está aprovisionado para empujar a un repo hoy y ese perfil corre Miasma hoy, una escritura aprobada pasa. Las concesiones del intermediario funcionan porque son estrechas. Un perfil que solo necesita leer un repo no debería poder escribirlo; un perfil que nunca publica no debería guardar ningún token de publicación. El aislamiento contiene la explosión; el ámbito decide cuán grande podría haber llegado a ser.
El siguiente repo de confianza ya está clonado en algún sitio.
La lección de el gusano de TanStack
fue que el lockfile y la firma no son defensas. La lección de el ámbito
de Red Hat fue que tampoco lo es
el editor. Microsoft añade el siguiente corolario: tampoco lo es el
repositorio, y el disparador ni siquiera tiene que ser una
instalación que elegiste — puede ser una carpeta que tu agente abrió.
El repo Azure/functions-action no hizo nada malo por ser de
confianza. Ser de confianza es el propósito entero de una Action
canónica de primera mano, y es exactamente lo que lo hizo digno de
envenenar — dos veces, en el caso de durabletask.
No puedes arreglar eso confiando con más cuidado, porque la confianza nunca estuvo mal puesta. Lo arreglas disponiendo las cosas de modo que «qué repo es este» y «qué ámbito publicó esto» dejen de ser las preguntas de las que depende tu keychain. Bromure Agentic Coding es la configuración donde el agente abre el repo dentro de una VM por perfil, las credenciales reales se quedan en el host detrás de un intermediario, cada escritura que el agente hace tiene que pasar un aviso, y un paquete no se puede instalar hasta que haya sobrevivido a un período de espera. Lo peor que un abrir-carpeta envenenado puede hacer es exfiltrar una caja que nunca tuvo tus llaves. Es gratis, open-source, y se entrega hoy.