Retour à tous les articles
Publié le · par Renaud Deraison

git status a exécuté leur code

Le 2 septembre 2026, Manifold Security a divulgué GitSpawn : huit failles réparties sur sept agents de codage en ligne de commande, où le .git/config d'un dépôt exécute une commande sur la machine du développeur. L'agent lance git status pour savoir sur quelle branche il se trouve, git rafraîchit son index, et la commande choisie par le dépôt s'exécute avec les privilèges de l'utilisateur connecté, hors du bac à sable, avant l'invite de confiance, et sur un outil avant même la connexion. Aucune invite, aucun appel d'outil, aucun tour de modèle. Bromure Agentic Coding ne corrige pas ce chemin. Il déplace la machine à laquelle ce chemin mène.

Vous n'avez rien tapé et rien approuvé. L'agent n'avait pas fini de démarrer, et sur l'un de ces outils vous n'étiez même pas connecté. Un dossier que quelqu'un vous a envoyé a exécuté une commande en votre nom, et c'est git qui l'a exécutée.

Un prestataire termine un travail et vous envoie un zip du répertoire du projet dans un dossier partagé. Vous le dézippez, vous faites cd dedans, et vous démarrez votre agent de codage pour vous orienter. L'agent commence par demander à git de quelle branche il s'agit et ce qui est modifié. C'est tout l'exploit.

Le 2 septembre 2026, The Hacker News a rapporté une divulgation de Manifold Security couvrant huit failles réparties sur sept agents de codage en ligne de commande, publiée sous le nom de GitSpawn. À eux tous, les outils concernés totalisent près d'un demi-million d'étoiles GitHub, et Claude Code à lui seul a dépassé les 77 millions de téléchargements npm par mois. Quatre des huit exécutaient encore des commandes fournies par le dépôt quand Manifold les a retestés le 1er septembre.

Un réglage de performance dont la valeur est une commande

core.fsmonitor est une option de git destinée aux gens qui ont des dépôts énormes. Plutôt que de parcourir tout l'arbre pour savoir ce qui a changé, git peut appeler un programme auxiliaire externe et le lui demander. La valeur du réglage est la commande. Git la lit dans le .git/config du dépôt lui-même et l'exécute chaque fois qu'il rafraîchit son index, c'est-à-dire sur git status, sur git diff, sur la plomberie sur laquelle s'appuie chaque outil.

Presque tous les agents examinés par Manifold rassemblent le contexte du projet en lançant ces commandes au démarrage, et aucun d'eux ne regardait d'abord le .git/config. Le dépôt nomme donc la commande et git l'exécute. Dans les mots des chercheurs, elle s'exécute « avec l'intégralité des privilèges de l'utilisateur connecté, hors du bac à sable ». OpenAI, dans son propre avis de sécurité pour la variante Codex, a indiqué que l'auxiliaire « s'exécute hors du bac à sable de commandes de Codex et sans invite d'approbation de l'utilisateur ».

Un git clone n'emporte rien de tout cela. Le clone transfère des objets et des références, et la configuration locale du dépôt source reste chez elle. Le projet empoisonné doit donc vous parvenir sous forme de fichiers, avec son répertoire .git intact : une archive zip, un disque partagé, un dossier synchronisé, une clé USB. La liste de Manifold décrit la façon dont les consultants, les clients et les collègues se transmettent du travail, et c'est le seul chemin de livraison où il n'y a aucune pull request. La protection de git contre les dépôts hostiles, safe.directory, vérifie si le dépôt appartient à quelqu'un d'autre. C'est vous qui l'avez dézippé, il vous appartient donc, et la protection n'a rien à dire.

Le calendrier place la protection propre à chaque agent une étape trop tard. Sur Claude Code et Hermes Agent, la charge se déclenche avant l'invite de confiance de l'espace de travail, la boîte de dialogue qui existe précisément pour demander si vous faites confiance à ce répertoire. Sur Qwen Code, elle se déclenche avant que l'utilisateur se soit authentifié, et sur Grok Build, elle se déclenche à la première frappe.

GitSpawn — de la remise au shellle dossier arrivezip, disque partagé,dossier synchro, USB —.git/config intactl'agent démarrecollecte le contexte :git status · git diffgit rafraîchit son indexlit le fichier du dépôtcore.fsmonitor = …<leur commande>votre user, votre env,votre répertoire homeCe qui n'avait pas encore eu lieul'invite de confianceClaude Code,Hermes Agentla connexionQwen Code se déclencheavant l'authentificationtaper quoi que ce soitGrok Build part à lapremière frappele modèleaucun appel d'outil, aucuntour, rien à journaliserChaque garde-fou de la rangée ci-dessus est une étape du démarrage de l'agent. La commande s'est exécutée avant.
Toute la chaîne, et les quatre choses qui n'avaient pas encore eu lieu quand elle s'est achevée. Le .git/config d'un dépôt nomme une commande dans core.fsmonitor ; l'agent lance git status pour déterminer sa branche ; git rafraîchit l'index et appelle l'auxiliaire. Celui-ci s'exécute en tant qu'utilisateur connecté, avant l'invite de confiance sur Claude Code et Hermes Agent, avant l'authentification sur Qwen Code, et à la première frappe sur Grok Build, sans aucun appel d'outil ni aucun tour de modèle nulle part dans la séquence.

Huit constats, sept agents, quatre encore ouverts

La moitié est corrigée. goose 1.44.0 embarque le correctif de CVE-2026-72718, notée CVSS 7.0. OpenAI a livré Codex CLI 0.131.0 et les builds bureau 26.519.x pour CVE-2026-19592. Cursor a corrigé sa variante. Manifold a confirmé le chemin core.fsmonitor de Claude Code en 2.1.193 et Anthropic l'a fermé en 2.1.196, sans publier d'avis de sécurité : Manifold note qu'aucun des deux constats concernant Claude Code n'apparaît dans les registres d'avis de npm.

L'autre moitié n'a pas bougé. Hermes Agent a un CVE (CVE-2026-71963) et un avis privé toujours non trié après six tentatives de contact. Les mainteneurs de Qwen Code ont accepté le rapport le 7 juillet et 0.22.3 exécute toujours la commande. Grok Build l'exécute toujours. Et Claude Code a un second chemin, via sa commande ultrareview, qui détourne une autre clé de configuration git et était toujours actif en 2.1.252. Manifold a retenu le nom de cette clé plutôt que de distribuer un modèle d'attaque réutilisable.

Anthropic a corrigé le premier chemin en trois jours. La classe de failles l'a contourné par une autre clé, dans le même fichier, dans le même produit.

C'est la surface d'exécution de git, héritée par tous les outils qui délèguent à git, et git a plus d'un réglage dont la valeur est une commande qu'il exécutera pour vous. Les pagers, les éditeurs, les commandes SSH, les chemins de hooks, les pilotes de diff et de filtre, et les alias commençant par une échappée shell prennent tous des valeurs de commande. Chaque éditeur doit maintenant déterminer, clé par clé, lesquelles un dépôt peut atteindre depuis son chemin de démarrage, face à un schéma de configuration qui gagne des clés avec le temps. Le correctif recommandé par Manifold, assainir la configuration git pendant les opérations de contexte en arrière-plan et désactiver core.fsmonitor, est une liste de refus maintenue face à un espace de noms. Les chercheurs ont trouvé le motif dans plus d'agents que le rapport n'en nomme.

Les contrôles qui auraient dû rattraper ça sont en aval

Alignez ce dont dispose aujourd'hui une équipe rigoureuse sur un agent de codage : les invites de permission sur l'usage des outils, la confiance de l'espace de travail, les hooks qui inspectent une commande avant son exécution, les points d'accès de transcript qui expédient une session vers un backend de conformité, et OpenTelemetry pour les décisions de permission et les contournements.

Tous s'ancrent à la boucle propre de l'agent, c'est-à-dire à un appel d'outil que l'agent a décidé de faire ou à un tour que le modèle a pris. GitSpawn ne produit ni l'un ni l'autre. Le processus qui exécute la charge est git, invoqué par l'agent pour sa propre intendance, et la commande vient d'un fichier que l'agent n'a jamais lu. Il n'y a aucun appel d'outil à approuver et aucun hook à déclencher. Une équipe qui regarde les transcripts d'agent atterrir dans son SIEM voit une session qui démarre normalement et ne montre rien, le même angle mort dont nous parlions quand Anthropic a livré son API Compliance : un journal ancré à ce qui a atteint le modèle ne peut pas voir ce qui s'est exécuté avant que le modèle soit consulté.

La commande va s'exécuter. La question qui vaut la peine d'être posée, c'est ce qu'elle va trouver.

Dézippé dans votre répertoire homegit status → auxiliaire core.fsmonitorchoisi par le dossier, exécuté en votre nomce qui est à un répertoire de là~/.ssh/id_ed25519ANTHROPIC_API_KEY, AWS_SECRET_ACCESS_KEY~/.git-credentials, ~/.aws, ~/.kube/config~/.zshrc — persistance à la prochaine sessionsortant : une connexion normale depuis unoutil de dev normal, sur un port normalle journal de l'agent n'en a aucune ligneLe dossier n'était pas de confiance. Le compte, si.Ouvert dans un espace de travail Bromuregit status → auxiliaire core.fsmonitors'exécute quand même — en ubuntu, dans l'invitéVM Linux jetable · démarrée avant le dossieraucun octet de clé privée — le ssh-agent hôte signeidentifiants env et config : des leurres (brm_…):80 et :443 détournés vers le proxy hôte, non désactivablepare-feu de sortie : deny par défaut → pas de 2e étageun leurre hors de sa portée → 451, VM en pause,espace signalé, effacement requis pour redémarrerchaque requête a sa ligne, quel que soit le processusLa commande s'exécute quand même. Dans un lieu prévu pour ça.
Le même dossier, le même git status, deux machines différentes. Sur votre portable, l'auxiliaire s'exécute en votre nom, à un répertoire de vos clés SSH et de vos identifiants cloud, et sa connexion sortante est une connexion ordinaire depuis un outil de développement ordinaire. Dans un espace de travail Bromure, il s'exécute en tant qu'ubuntu dans une VM démarrée avant que le dossier existe : aucun octet de clé privée à voler, des leurres là où devraient être les identifiants, et chaque octet de sortie sur un fil que l'hôte possède et enregistre.

Exécutez-la et voyez ce qu'elle trouve

Bromure Agentic Coding donne à chaque espace de travail sa propre VM Ubuntu sous l'hyperviseur d'Apple. L'agent, son git, son gestionnaire de paquets, et tout ce que ces trois-là démarrent vivent dans l'invité. Déposez le zip du prestataire dans un espace de travail, laissez l'auxiliaire se déclencher, et accordez tout à l'attaquant : avant la confiance, avant l'authentification, root dans l'invité si vous voulez.

Lire les clés SSH. Il n'y en a aucune à lire. Les clés privées d'un espace de travail restent sur votre Mac ; la VM reçoit un SSH_AUTH_SOCK ponté par vsock vers un agent côté hôte. Ce protocole a une requête qui signifie « signe ce défi » et aucune requête qui signifie « donne-moi la clé ».

Balayer l'environnement à la recherche d'identifiants. Ils y sont tous, formatés comme les validateurs l'attendent, et tous sont des leurres. ANTHROPIC_API_KEY contient un faux sk-ant-api03-brm-…, et il en va de même pour ~/.git-credentials, ~/.aws/config, ~/.kube/config et ~/.docker/config.json. Vos vraies valeurs vivent chiffrées sur votre Mac, et le proxy les substitue sur le fil après que la requête a quitté la VM, uniquement lorsqu'elle est destinée à l'hôte pour lequel cet identifiant a été frappé. Aucun interrupteur ne désactive cela. C'est ainsi que fonctionnent les identifiants dans l'application.

Télécharger le second étage. L'auxiliaire est un chargeur, quelques centaines d'octets dont le travail est d'aller chercher la vraie charge. Cette requête se heurte au pare-feu de sortie de l'espace de travail. Réglez le Trafic non apparié sur Deny et la VM atteint les hôtes que vous avez listés et rien d'autre, appliqué à deux endroits : le commutateur virtuel filtre par IP de destination et par nom d'hôte reniflé dans le DNS, tous protocoles confondus, et le proxy filtre à nouveau par nom de serveur TLS et, pour les règles web, par méthode HTTP. Un chargeur qui se tourne plutôt vers un registre de paquets rencontre le pipeline de chaîne d'approvisionnement, où la barrière d'âge refuse les versions plus jeunes que le seuil. Une charge publiée hier ne peut pas franchir cette barre.

Exfiltrer ce qu'il a trouvé. Là, quelque chose doit traverser un fil que votre Mac possède. Le proxy analyse chaque requête sortante par rapport aux leurres frappés pour l'espace de travail, en-têtes et corps, avec un automate d'Aho-Corasick. Un leurre adressé à un hôte hors de la portée pour laquelle il a été frappé est la signature d'une machine qui envoie quelque chose qu'elle ne devrait pas connaître. Le proxy bloque avec un HTTP 451 avant qu'un seul octet n'atteigne la destination, met la VM en pause, et lève une alerte nommant l'identifiant et les deux hôtes. Bromure marque ensuite l'espace de travail comme compromis, et il ne redémarrera pas tant que vous n'aurez pas effacé son disque et son home.

Laisser de la persistance. Un .bashrc modifié dans l'invité dure aussi longtemps que vous laissez durer la VM. Effacer le home réinitialise /home/ubuntu, Réinitialiser le disque reclone le disque système depuis l'image de base signée, et bromure-cli vm run --rm vous donne un espace de travail qui se supprime lui-même à l'arrêt de la VM.

L'interception n'a aucune variable d'environnement à désactiver

Un proxy auquel l'invité adhère est un proxy que l'invité peut décliner. Le commutateur virtuel de Bromure détourne lui-même les flux port 80 et port 443 de la VM vers le proxy hôte : aucun HTTPS_PROXY à désactiver, rien dans l'invité qui l'éteigne, et le HTTP en clair inspecté comme l'est le HTTPS. Le processus qui fait la requête ici n'a jamais lu la configuration de l'agent et n'est pas l'agent. C'est un binaire auxiliaire lancé par git, et il emprunte le même fil que tout le reste.

La ligne existe même si aucun appel d'outil n'existe

Le proxy hôte écrit un enregistrement de métadonnées pour chaque requête sortant de la VM, quel que soit le processus qui l'a faite : heure, hôte, port, méthode, chemin, statut, latence, et octets mesurés avant la substitution de l'identifiant. Les verdicts du pare-feu, les décisions de chaîne d'approvisionnement et le courtage d'identifiants atterrissent dans la Chronologie de sécurité (Fenêtre → Chronologie de sécurité…), et sur un Mac inscrit ils partent vers le flux d'événements de l'organisation sous egress.firewall, supply_chain.fetch et credential.exfiltration. Un transcript côté agent de cette attaque est vide pendant que celui du fil porte la requête.

L'invité peut atteindre une seule chose sur votre Mac. Un dossier partagé est une fenêtre vivante, en lecture-écriture, sur votre vrai système de fichiers, et l'effacement pour compromission le laisse intact par conception. Partagez le répertoire du projet plutôt que votre répertoire home, et le find de l'auxiliaire pour les *.pem parcourt un home Linux plein de checkouts et de caches de paquets au lieu de vos connexions adossées au trousseau.

La frontière qui n'est pas une étape de la séquence

Lisez ensemble les quatre détails de calendrier de GitSpawn — avant l'invite de confiance, avant l'authentification, avant la première frappe, avant tout tour de modèle — et ils disent une seule chose. Chaque protection est une étape d'une séquence de démarrage, et chaque nouvelle fonctionnalité ajoute une étape devant elle. Cursor l'a appris en 2025 avec un mcp.json fourni par le dépôt, et de nouveau en juillet dernier avec une commande de mise en place de worktree qui s'exécutait avant sa propre invite de confiance. C'est maintenant le tour de git, dans sept produits à la fois, via une clé de configuration présente dans git depuis des années.

Un hyperviseur n'est pas une étape de cette séquence. Il ne lit pas le .git/config. Aucune refactorisation ne le réordonne et aucun chemin de code ne s'en exempte, parce qu'il ne s'exécute pas dans le même programme que l'invite. La VM tournait avant que le zip ait fini de se télécharger, et ses deux garanties — aucun vrai secret ici et aucune route non médiée vers l'extérieur — tiennent aussi bien en 2.1.193 qu'en 2.1.196.

Vérifiez votre .git/config avant d'ouvrir un dossier que quelqu'un vous a envoyé. Manifold a raison de le dire. Vous le ferez aussi pendant deux semaines puis vous sauterez l'étape au troisième zip d'un après-midi chargé, ce qui est la durée de vie de la plupart des instructions qui se terminent par « avant de l'ouvrir ». Installez Bromure Agentic Coding, donnez au dossier inconnu sa propre machine, et laissez la config exécuter ce qu'elle veut dans une pièce où s'exécuter est tout ce qu'elle pourra faire.