Une session, cent dépôts
Le rapport AI Risk and Resilience 2026 de Mandiant décrit un attaquant qui a pris le contrôle d'une session d'assistant de code en cours chez un fournisseur SaaS, y a fait installer un paquet PyPI piégé, volé des jetons OAuth GitHub et propagé le ver Shai-Hulud dans une centaine de dépôts internes. Le rapport ne dit pas comment la session a été prise. Dans un workspace Bromure Agentic Coding, il n'a pas besoin de le dire : le jeton présent dans cette session est un leurre `ghp_`, la tentative de l'exfiltrer met la VM en pause, et les push qui transforment une machine en cent dépôts s'arrêtent au proxy de l'hôte.
Quelqu'un a pris le contrôle d'une session de code qui tournait, authentifiée et de confiance, sur la machine même d'un développeur. Tout ce qui a suivi, jusqu'à une centaine de dépôts, est sorti de ce que cette machine détenait à cet instant.
Un développeur d'une entreprise de logiciel en tant que service a demandé à son
assistant de code quel paquet utiliser pour un problème ordinaire. L'assistant
en a nommé un. Le développeur a dit oui, ce qui est tout l'intérêt d'avoir un
assistant, et pip install a fait le reste.
Un attaquant tenait déjà la session, et c'est lui qui a tapé cette recommandation.
Mandiant décrit le cas dans son AI Risk and Resilience Report 2026, publié ce mois-ci par la branche réponse à incident de Google Cloud ; The Hacker News l'a relayé le 16 septembre. L'attaquant a détourné une session d'assistant de code active sur le poste d'un développeur. Cinq étapes de plus l'ont mené à une centaine de dépôts de code internes et à l'espace de noms de paquets de l'entreprise, où un second employé a récupéré la version empoisonnée.
La chaîne, maillon par maillon
Mandiant retient précisément ce que vous voudriez le plus savoir. L'étude de cas publique ne dit pas quand l'intrusion a eu lieu, et elle ne dit pas comment l'attaquant a pris le contrôle d'une session en cours. Cookies de navigateur volés, extension malveillante, CLI compromise sur une machine partagée : le rapport n'en retient aucun. Qui le lit pour trouver le correctif repartira les mains vides.
Les étapes 2 à 6 ne contiennent aucun exploit. Une installation de paquet
fonctionne comme fonctionnent les installations de paquets, et du code exécuté
sous l'identité du développeur lit les jetons posés sur le disque du
développeur. Un git push muni d'un jeton valide pousse, et une publication
munie de droits de publication valides publie. Une étape était une intrusion ;
les cinq autres étaient de la dépense.
Le multiplicateur, ce sont les identifiants, et il ne cesse de grandir
Shai-Hulud est le ver auto-répliquant de la chaîne d'approvisionnement qui
dévore des comptes de mainteneurs depuis 2025, et il apparaît comme charge utile
dans une histoire pareille parce que l'étape 5 est exactement ce pour quoi ses
auteurs l'ont construit. GitGuardian a disséqué une variante récente en août et
l'a trouvée à ratisser 469 emplacements d'identifiants,
contre 189 dans les versions antérieures : environnements de développement,
outillage CI/CD, configuration cloud, configurations d'outils d'IA, fichiers de
configuration des gestionnaires de paquets, historique du shell, fichiers
.env, réglages d'IDE, caches de CLI. Le ver lit un classeur plutôt qu'il ne
cherche une faille.
L'analyse de GitGuardian résume la catégorie en une phrase :
« Les attaquants ont cessé d'essayer de briser les relations de confiance et se sont mis à utiliser les identifiants qui font déjà fonctionner ces relations. »
Un poste de travail est devenu cent dépôts parce qu'il détenait de quoi autoriser cent dépôts. Ce nombre est sorti d'un inventaire, et c'est vous qui décidez de ce qui entre dans l'inventaire.
La session ne détenait que des leurres
Bromure Agentic Coding exécute l'agent dans une machine virtuelle Linux sur votre Mac, et cette machine ne détient aucun secret réel. Vous n'avez rien à activer pour cela. C'est la façon dont un workspace stocke un identifiant, tout simplement.
Votre vrai jeton GitHub reste chiffré sur l'hôte. La VM reçoit un faux qui
préserve la structure : ghp_ suivi de 36 caractères, 40 en tout, si bien que
les contrôles de préfixe et de longueur de gh passent sans broncher. Bromure
l'exporte comme GH_TOKEN et l'écrit dans ~/.git-credentials et dans la
configuration de gh ; c'est le seul identifiant GitHub présent dans cette
machine. Un proxy sur l'hôte substitue la vraie valeur sur le fil après que la
requête a quitté la VM, et seulement quand la destination correspond à l'hôte
pour lequel l'identifiant a été forgé. Chaque faux est déterministe, dérivé de
la vraie valeur et d'un sel de 32 octets propre à l'installation via HKDF-SHA256,
de sorte qu'un client qui prend l'empreinte de sa propre clé ne voit aucune
rotation apparente d'une session à l'autre.
Passez l'étape 4 là-dessus. Le voleur de données arrive, ratisse les 469
emplacements, trouve la variable d'environnement, trouve ~/.git-credentials,
trouve la configuration de gh, et expédie sa récolte. Chaque lecture réussit,
et chaque fichier se trouve là où le ver l'attendait. L'attaquant se retrouve
avec une chaîne de quarante caractères qui n'authentifie nulle part, sauf à
repasser par un Mac bien précis.
Les étapes 5 et 6 ont toutes deux besoin que l'étape 4 ait produit un jeton fonctionnel, et l'étape 4 a produit un leurre.
Le vol est aussi l'alarme
Un faux jeton n'a qu'une destination légitime. Aucune requête innocente ne
transporte un leurre ghp_ forgé pour github.com vers un autre hôte ; le
proxy inspecte donc chaque requête sortante, en-têtes et corps, à la recherche
d'un faux qui quitterait son périmètre. Il fait tourner un automate
d'Aho-Corasick, assez peu coûteux pour être braqué sur l'ensemble du trafic.
Quand le proxy en trouve un, il fait plus que journaliser :
- il refuse la requête avec un HTTP 451, et pas un octet n'atteint la destination ;
- Bromure met la VM en pause sur-le-champ ;
- une alerte vous propose Éteindre, Conserver pour investigation (exporter d'abord le disque, le dossier personnel et les dossiers partagés pour l'analyse forensique) ou Continuer à vos risques et périls ;
- la Security Timeline gagne une ligne rouge Credential brokering ;
- Bromure marque le workspace comme compromis, de sorte que le prochain lancement efface l'image disque et le dossier personnel persistant. Vos jetons, vos clés SSH et vos réglages de workspace survivent à cet effacement.
Vous n'activez rien de tout cela ; le détecteur est toujours en service. C'est la tentative d'exfiltration de l'étape 4 qui arrête la session, et l'incident se termine donc sur une VM en pause et une entrée de timeline plutôt que sur une centaine de dépôts et une étude de cas.
Cent dépôts, cela fait cent push
Supposons que vous vouliez la ceinture en plus des bretelles. L'étape 5, c'est un ver qui écrit dans des dépôts, et l'hôte classifie les écritures.
Les garde-fous d'un workspace portent une politique
d'écriture par service. Pour GitHub, cette politique lit le git-over-HTTPS aussi
bien que l'API REST : un git push arrive en git-receive-pack et compte comme
une écriture, tandis qu'un git fetch arrive en git-upload-pack et compte
comme une lecture. Lecture seule refuse les push. Demander avant
d'écrire, le réglage par défaut des nouveaux workspaces, retient chacun d'eux
pour une boîte de dialogue côté hôte qui nomme l'opération. Les fetch passent
dans les deux cas, donc l'agent continue de travailler.
Le proxy tranche sur l'hôte, à l'extérieur de la VM. Un agent dont la session appartient à quelqu'un d'autre ne peut ni désactiver la politique ni la contourner, parce que la politique ne s'exécute pas du côté de l'agent. Un ver qui veut cent dépôts récolte cent refus, ou vous pose cent fois la question.
Et le paquet devait encore arriver
L'étape 3 est l'endroit où Mandiant donne un conseil concret : valider les dépendances tierces recommandées par l'IA contre des sommes de contrôle cryptographiques et des listes d'autorisation, et faire transiter le trafic de dépendances par des dépôts que vous contrôlez.
Dans un workspace, chaque récupération de paquet passe déjà par l'hôte, puisque
le proxy est la seule route de la VM vers le réseau. Bromure intercepte PyPI sur
pypi.org et files.pythonhosted.org et y applique la politique de l'hôte. Le
pip.conf présent dans la VM peut resserrer ce que le proxy a servi ; il ne
peut pas le desserrer. Trois couches ont voix au chapitre pour les paquets
Python :
- la barrière d'âge, active par défaut avec un minimum de deux jours, qui refuse les versions plus jeunes que le seuil, au motif qu'une publication toute fraîche est celle qui a le plus de chances de venir d'être détournée. Les index PEP 503 ne portent pas d'horodatage, alors Bromure va chercher la date de publication à la demande ;
- le contrôle OSV auprès d'
api.osv.dev, gratuit et sans clé, qui bloque toute version portant un avis de sécurité au niveau de gravité que vous choisissez ou au-dessus ; - le filtre de paquets compromis de socket.dev, qui se déclenche sur les malwares, les malwares connus, le typosquatting et les scripts d'installation malveillants.
Un blocage revient sous forme d'un HTTP 451 dont le corps commence par
Bromure Supply-Chain Security blocked this request: suivi du motif, et pip
l'affiche mot pour mot. Vous voyez pourquoi l'installation a échoué, et l'agent
aussi, qui sait souvent épingler tout seul une version plus ancienne. Cette
version plus ancienne est précisément celle vers laquelle la barrière d'âge le
poussait.
Les trois contrôles de Mandiant, et où ils habitent
Le rapport clôt l'étude de cas sur trois recommandations. Chacune nomme un endroit où un workspace Bromure place déjà la frontière.
Valider les dépendances recommandées par l'IA
Mandiant réclame des sommes de contrôle et des listes d'autorisation sur tout ce que l'assistant suggère. Votre workspace applique sa politique à la réponse du registre avant que la VM ne la voie. Le proxy retire du listing de versions celles qui sont trop fraîches, si bien que du point de vue de l'agent elles n'existent pas encore, et il refuse une version trop fraîche épinglée en citant dans l'erreur l'âge réel du paquet. Les clés des services de réputation restent sur l'hôte, en dehors de la machine qui exécute l'installation.
Tenir les clés brutes et les jetons durables à l'écart
C'est la frontière du fil, écrite sous forme de conseil. Aucun fichier, aucune variable d'environnement, aucun processus dans la VM ne détient de vraie clé d'API, de jeton OAuth, de secret AWS ou de clé privée SSH. Bromure garde les jetons OAuth d'abonnement chiffrés sur l'hôte et les renouvelle là-bas environ cinq minutes avant l'expiration, de sorte que l'invité ne porte aucun jeton de rafraîchissement. Le canal entre les deux ne va que dans un sens : l'hôte y écrit des faux, et la VM ne dispose d'aucun appel pour redemander un vrai jeton.
Faire passer le trafic de dépendances par ce que vous contrôlez
Le proxy est la seule route de la VM vers l'extérieur. Chaque requête HTTPS voyage par un socket virtuel jusqu'à l'hôte, qui termine le TLS, inspecte la requête et la réémet à travers la pile TLS de macOS. C'est ce chemin unique qui rend les deux autres contrôles exécutoires plutôt que consultatifs. Aucune récupération de paquet ne peut sauter les vérifications, et aucun identifiant ne peut partir sans être substitué.
Et réduire le rayon d'explosion, délibérément
Cent dépôts sont tombés parce qu'une machine portait de quoi autoriser cent dépôts. Découpez les workspaces un par projet, ou un par frontière d'identifiants. Chacun correspond à une seule VM, deux workspaces n'en partagent jamais une, et chacun porte les identifiants dont le travail de ce workspace a besoin. L'inventaire d'une machine donnée se règle dans un panneau de préférences.
La partie que vous ne pouvez pas corriger
Mandiant cadre la catégorie en une phrase qui décrit la forme plutôt que l'incident : « une source de données empoisonnée, une dépendance de modèle ou un point d'accroche d'extension peut transformer un agent de confiance en canal de reconnaissance interne, de déplacement latéral ou d'évasion autonome hors d'un bac à sable. »
Trois points d'entrée différents, une seule phrase, et elle vaut pour les trois. Le point d'entrée détermine la façon dont un attaquant arrive ; le contenu de la machine d'un développeur détermine jusqu'où il va. Cet attaquant-ci est entré par une session détournée. Le suivant entrera par autre chose, et le rapport ne nomme aucune porte que vous puissiez verrouiller.
Construisez pour l'étape d'après l'entrée. Supposez que la session appartient déjà à quelqu'un d'autre, ce que l'étude de cas vous impose de supposer en n'expliquant jamais l'étape 1, puis demandez-vous ce que cette session peut atteindre.
Une version de cet incident s'arrête à l'étape 3, sur un 451 dans le terminal et une ligne dans le Security Log. Une autre s'arrête à l'étape 4, sur une VM en pause et une ligne rouge dans la timeline consignant qu'un identifiant a tenté de sortir. Aucune des deux n'atteint l'étape 5, et c'est à l'étape 5 que se trouvaient les cent dépôts.
Mettez l'agent sur une machine qui ne détient que des leurres. Installez Bromure Agentic Coding, et laissez quelqu'un prendre la session.