Le package était bel et bien celui de Red Hat
Entre fin mai et le 1er juin 2026, un ver appelé Miasma a poussé du code voleur de credentials dans 32 packages sous le scope npm @redhat-cloud-services — l'espace de noms même de Red Hat, ~117 000 téléchargements hebdomadaires, signés par le vrai pipeline de publication de Red Hat. Il n'y avait aucun typosquat à attraper et aucun mainteneur inconnu à signaler. Le signal de confiance était le nom du fournisseur sur le scope, et le nom du fournisseur est exactement ce sur quoi l'attaquant est entré. Voici pourquoi « préférez les éditeurs réputés » a cessé d'être une défense, et ce qui change quand l'agent qui exécute l'installation vit dans une VM Bromure par profil.
Les attaques de supply-chain dont nous avons parlé en mai avaient un
indice révélateur. Une URL Git là où une plage de versions aurait dû
se trouver. Un runtime Bun surgissant de nulle part. Une dépendance
optionnelle qui échoue exprès. Miasma n'a aucun indice révélateur.
Les packages étaient @redhat-cloud-services/sources-client et
trente et un de ses frères — authentiquement ceux de Red Hat, sous
le scope npm propre à Red Hat, publiés par le pipeline propre à Red
Hat avec une signature valide. Rien n'était falsifié. L'attaquant
n'avait besoin de rien falsifier. Le nom sur le scope était tout
l'exploit.
Un agent de codage qui résout
@redhat-cloud-services/vulnerabilities-client n'hésite pas, et vous
non plus. C'est une dépendance de première partie de l'un des plus
grands fournisseurs d'entreprise de l'industrie. Il n'y a aucun
mainteneur à vérifier, parce que le mainteneur est Red Hat. Il n'y a
aucun nom à scruter pour y trouver une lettre transposée, parce que le
nom est épelé exactement comme il devrait l'être. Chaque heuristique
qu'un développeur soigneux ou un agent soigneux applique avant
d'exécuter npm install revient au vert. Donc l'installation tourne,
et un hook preinstall se déclenche, et le hook est un blob de
4,2 mégaoctets de JavaScript obfusqué qui se met à lire le système de
fichiers à la recherche de clés.
L'incident tout entier est une démonstration d'un fait inconfortable : éditeur réputé n'est pas un contrôle de sécurité. On a l'impression que ç'en est un. La plupart des conseils de supply-chain des trois dernières années s'appuient dessus. Et le 29 mai, ça n'a strictement rien acheté.
Ce que Miasma a fait.
La campagne — la chaîne Miasma: The Spreading Blight apparaît pour
la première fois dans un commit daté du 29 mai 2026, selon
OX Security —
a été attrapée par Aikido et OX Security et
analysée plus tard par Socket, JFrog, Wiz, ReversingLabs, Microsoft et
d'autres. BleepingComputer
et The Hacker News
l'ont tous deux couverte le 1er juin.
La forme, réduite à sa mécanique :
- Le point d'entrée était un compte GitHub d'employé de Red Hat
compromis, utilisé pour pousser des commits malveillants dans les
dépôts source
@redhat-cloud-services. - Un workflow GitHub Actions transportait un script
_index.jsqui s'authentifiait à l'endpoint de publication de confiance de npm en utilisant un token OIDC — le même mécanisme sans clé que npm recommande désormais plutôt que les tokens de publication à longue durée de vie. Du côté de npm, la CI de Red Hat a publié les packages de Red Hat. La signature était réelle. - Les packages publiés portaient un hook
"preinstall": "node index.js"et une charge utile obfusquée d'environ 4,2 Mo. - À l'installation, la charge utile balayait à la recherche de
secrets GitHub Actions, credentials AWS, credentials Google Cloud,
service principals Azure, tokens HashiCorp Vault, tokens de compte de
service Kubernetes, tokens de publication npm et PyPI, clés SSH,
credentials Docker, clés GPG et fichiers
.env— puis chiffrait et exfiltrait tout ce qu'elle trouvait. - Il s'auto-propageait en utilisant l'accès volé pour commiter via
l'API GitHub, en lisant les fichiers
action.ymlvia GraphQL et en réécrivant de nouveaux workflows à travers des mutations de sorte que les changements apparaissaient, selon les propres mots de Red Hat dans le journal des commits, verified et signed.
Au total, 32 packages sur 96 versions ont été touchés, des packages
totalisant environ 117 000 téléchargements hebdomadaires, et la
campagne plus large a touché 309 dépôts GitHub. L'évaluation de
Socket était que ceci est « effectivement une campagne Mini
Shai-Hulud : elle utilise les mêmes tactiques fondamentales
d'exécution au moment de l'installation, de récolte de credentials, de
ciblage CI/CD, d'exfiltration chiffrée et de propagation potentielle
en aval. » La déclaration de Red Hat était que « les packages sont
strictement limités au développement interne, et le code malveillant
n'a jamais été publié pour la consommation des clients » — ce qui est
vrai, et également pas d'un grand réconfort pour les développeurs et
les runners CI qui ont tiré @redhat-cloud-services/* comme dépendance
transitive dans la fenêtre avant que les packages ne soient retirés.
La défense que tout le monde recommande est celle qui a cédé.
Nous avons parlé d'un ver similaire
il y a trois semaines — la
compromission de TanStack, où le signe distinctif était un script
prepare accroché à une URL Git épinglée et un runtime Bun surgi de
nulle part. La leçon honnête de ce post était : ne faites pas confiance
au lockfile, ne faites pas confiance à la signature. Miasma est le tour
de vis suivant de la même vis, et il vaut la peine d'être précis sur ce
qui est différent, parce que la différence est tout l'enjeu.
La pile standard d'hygiène de supply-chain a trois échelons. Épinglez
vos versions. Vérifiez la provenance. Préférez les éditeurs réputés.
Miasma traverse les trois tout droit. L'épinglage ne fait rien, parce
que les versions malveillantes sont les versions publiées. La
provenance ne fait rien, parce que la provenance est valide — le flux
de publication de confiance OIDC était authentiquement la CI de Red
Hat, et en aval le ver a frappé des commits de workflow que GitHub
lui-même a marqués verified et signed. Et le troisième échelon,
préférez les éditeurs réputés, n'est pas seulement vaincu ici — il
est la surface d'attaque. La réputation du scope
@redhat-cloud-services est la raison pour laquelle les packages sont
tirés sans un second regard. Plus l'espace de noms est de confiance,
plus il est utile à quiconque s'en empare.
Il n'existe aucune version de « lisez le package plus attentivement » qui attrape ceci. Le package est correct. Le package est celui de Red Hat. Le problème, c'est que l'exécution de code au moment de l'installation avec les credentials ambiants du développeur est le contrat, et un nom de confiance ne change rien à ce que ce code peut toucher une fois qu'il tourne.
La même installation à l'intérieur de Bromure Agentic Coding.
Bromure Agentic Coding fait tourner votre agent de
codage à l'intérieur d'une VM Linux par profil — son propre noyau,
son propre système de fichiers, sa propre pile réseau, sur le framework
Virtualization d'Apple. Un profil est un périmètre de travail cohérent :
ce client, ce produit interne, cette bibliothèque open-source.
L'agent y fait ses npm install, et l'hôte — votre vrai trousseau, vos
vrais credentials cloud, vos vraies clés SSH — est de l'autre côté
d'une frontière imposée par le matériel que le hook preinstall ne peut
pas franchir.
Les credentials ne vivent pas dans le profil. Ils vivent sur l'hôte,
derrière un courtier de credentials.
Quand l'agent a besoin de pousser un commit ou de publier un package,
il ne lit pas un token sur le système de fichiers de l'invité — il n'y
en a aucun à lire. Il demande au courtier, sur un Unix domain socket,
d'utiliser un credential en son nom. Le courtier détient la vraie clé
privée GitHub App, frappe un token d'installation à courte durée scopé
au dépôt sur lequel l'agent travaillait déjà, et — pour un profil
configuré pour l'exiger — fait apparaître une invite d'autorisation à
laquelle le développeur répond sur l'hôte avant que la requête ne
parte. Le token est frappé et attaché à la requête sortante entièrement
du côté hôte ; il n'entre jamais dans la mémoire ou le disque de
l'invité. Le principe, qui est aussi vieux que ssh-agent : courtisez
l'usage du credential, jamais sa valeur.
Alors parcourons le balayage de Miasma à travers cette frontière.
node index.js lit ~/.aws/credentials et trouve un stub ou rien. Il
lit ~/.npmrc et ne trouve aucun token de publication. Il lit
l'environnement à la recherche de $GITHUB_TOKEN et ne trouve rien à
voler — le token d'installation est frappé et dépensé sur l'hôte,
jamais écrit dans l'invité, et le courtier n'en frappe un que lorsque
le développeur répond à l'invite d'autorisation. Le trousseau GPG, la
clé privée SSH, le token Vault, le kubeconfig : côté hôte, courtisés
s'ils sont exposés tout court, absents sinon. La charge utile de
4,2 Mo s'exécute jusqu'au bout exactement comme prévu. Elle exfiltre
juste un invité qui n'a jamais détenu les clés du développeur.
Le push que le proxy refuse de forwarder.
Voler la clé n'est que la moitié de Miasma. L'autre moitié est la propagation : il a utilisé l'accès GitHub volé pour commiter des workflows empoisonnés via l'API, et c'est ce qui a transformé une mauvaise installation en 309 dépôts. Le courtier gère le vol. Les Guardrails, ajoutés dans Bromure Agentic Coding 2.0, gèrent le mésusage.
Les Guardrails sont un moteur de politique côté hôte qui vit à
l'intérieur du même proxy MITM par lequel le trafic courtisé transite
déjà, de sorte qu'un agent compromis dans l'invité ne peut pas les
contourner. Chaque requête est classée par ce qu'elle fait réellement à
la ressource, et chaque ressource — GitHub, AWS, Kubernetes, registres
Docker, DigitalOcean, GitLab, Bitbucket, bases de données hébergées —
peut être réglée sur off, bloquer les actions destructrices ou
lecture seule. Mettez le guardrail GitHub d'un profil en mode lecture
seule et un git push — le git-receive-pack dont le ver a besoin
pour réécrire son workflow — retourne un 403 net, tandis que
git fetch continue de fonctionner. Un DELETE contre l'API
Kubernetes, une suppression de manifeste dans un registre, un appel
Terminate* à EC2 : même traitement. L'agent voit juste un échec d'API
normal. L'étape de propagation de Miasma est une écriture que le proxy
refuse de forwarder, que l'agent ait jamais approché un vrai credential
ou non.
Et la persistance ?
C'est ici que le modèle par profil est honnête sur un coût. Miasma est un ver ; toute son ambition est de revenir. Sur un fantasme de disque jetable, on balaie cela d'un revers de main — le disque a disparu après la tâche. Un profil Bromure est de longue durée de vie, donc une charge utile qui s'écrit dans un script de démarrage à l'intérieur du profil peut survivre jusqu'à la prochaine session d'agent dans ce profil. Nous n'allons pas prétendre le contraire.
Ce dont cette persistance hérite, en revanche, c'est d'un invité sans clés d'hôte et d'un courtier qui ne parle qu'en tokens à courte durée et à portée limitée. Le ver se réveille dans le même sandbox où il est mort. Il peut lire les mêmes stubs. Il peut demander au courtier d'utiliser un credential pour le seul dépôt pour lequel ce profil est autorisé — et le développeur doit toujours répondre à l'invite d'autorisation pour que cela aboutisse, avec les Guardrails libres de refuser le push purement et simplement. Il ne peut pas atteindre le trousseau de l'hôte, les autres profils, ou les credentials cloud du développeur, parce que ceux-ci n'ont jamais été à l'intérieur de la frontière au départ. La persistance achète une présence continue dans une boîte qui ne contient rien.
Et chaque bribe de cela — le déclenchement du preinstall,
node index.js chargeant un blob de plusieurs mégaoctets, le fichier
écrit dans un chemin de démarrage, la tentative d'egress — atterrit dans
la trace de session au niveau de
l'hyperviseur. Quand Aikido
publie les indicateurs le lendemain matin, la question « est-ce que ce
profil a déjà exécuté Miasma ? » est un grep, pas un engagement de
réponse à incident.
Là où ceci ne vous sauve pas.
Le scope du courtier est tout le jeu.
Si un profil est provisionné pour publier vers votre scope npm aujourd'hui, et que ce profil installe Miasma aujourd'hui, le courtier le laissera publier. Le courtage fonctionne parce que l'octroi est étroit et de courte durée. Un profil qui n'a pas besoin de publier ne devrait pas pouvoir le faire. Scopez-le exprès.
Il ne revoit pas le diff.
Miasma s'est propagé en commitant des workflows qui apparaissaient verified et signed. Un guardrail GitHub en lecture seule bloque le push purement et simplement — mais un profil qui a légitimement besoin de pousser tourne en mode bloquer-les-destructrices, et les Guardrails classent par méthode, pas par ce qui est dans le diff. Ni l'isolation ni un guardrail au niveau de la méthode n'empêchent un agent de se faire convaincre de commiter un workflow empoisonné qu'il est autorisé à pousser. Lisez le diff. La trace vous dit quels diffs lire.
Le presse-papier est partagé par défaut.
Bromure est livré avec le partage de presse-papier hôte/invité activé, parce que coller une stack trace dans un chat est une chose que les humains font toute la journée. Pour un profil sensible, isolez le presse-papier. Le contrôle existe ; ce n'est juste pas le défaut.
La trace est un log d'audit, pas un IDS.
La trace de session enregistre le preinstall, le blob, l'egress. Elle ne décide pas, à elle seule, que la destination est hostile. Elle capture assez pour qu'une fois que quelqu'un nomme l'indicateur, votre réponse soit à deux secondes.
Le prochain scope est déjà de confiance.
La leçon du ver TanStack était que le lockfile et la signature ne sont
pas des défenses. Miasma ajoute le corollaire inconfortable : l'éditeur
non plus. Le scope @redhat-cloud-services n'a rien fait de mal en
étant de confiance — être de confiance est tout le but d'un espace de
noms de fournisseur, et c'est exactement ce qui l'a rendu digne d'être
attaqué. La prochaine campagne entrera sur un scope auquel vous faites
tout autant confiance, signé tout aussi valablement, par un pipeline
qui était authentiquement celui du fournisseur jusqu'au moment où il ne
l'était plus.
Vous ne pouvez pas régler cela en faisant confiance plus
soigneusement. Vous le réglez en arrangeant les choses de sorte que
« quel scope a publié ceci » cesse d'être la question dont dépend votre
trousseau. Bromure Agentic Coding est la
configuration où l'agent fait son installation à l'intérieur d'une VM
par profil, les vrais credentials restent sur l'hôte derrière un
courtier, et le pire qu'un hook preinstall puisse faire est
d'exfiltrer une boîte qui n'a jamais détenu vos clés. C'est gratuit,
open-source, et livré aujourd'hui.