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

Le symlink a quitté le workspace, et n'a rien trouvé à voler

GhostApproval a montré comment un dépôt hostile peut piéger six assistants de codage IA pour qu'ils écrivent et lisent des fichiers hors du dossier où ils étaient censés rester : implanter des clés SSH, détourner le démarrage du shell, lire des identifiants cloud sur la propre machine du développeur. Le bug est une erreur de suivi de symlink. S'il fait mal, c'est parce que l'agent tourne sur une machine pleine de vos secrets. Bromure Agentic Coding change ce second fait.

Un agent de codage est un programme que vous invitez à lire et écrire des fichiers sur votre portable. GhostApproval est l'histoire de ce qui arrive quand un dépôt hostile convainc ce programme d'écrire un peu en dehors des lignes : dans vos clés SSH, votre config shell, vos identifiants cloud. Le bug est vieux et banal. Les dégâts viennent de l'endroit où se tenait l'agent.

Le 8 juillet 2026, l'équipe de recherche de Wiz a publié GhostApproval, le compte rendu d'une même faille trouvée, sous six formes différentes, dans six assistants de codage IA populaires : Amazon Q Developer, Claude Code d'Anthropic, Augment, Cursor, Antigravity de Google et Windsurf. Les détails varient d'un produit à l'autre. La forme, non. Vous ouvrez un dépôt, peut-être un tutoriel que quelqu'un a partagé ou un projet qu'un collègue vous a demandé de regarder, et vous pointez votre agent de codage dessus. Quelque part dans ce dépôt se trouve un fichier qui n'est pas un fichier.

Un fichier qui n'est pas un fichier.

L'astuce est un lien symbolique, ou symlink : une entrée du système de fichiers qui ressemble à un fichier mais qui est un panneau indicateur pointant vers un autre chemin. Les symlinks sont une fonctionnalité Unix normale, vieille de quarante ans. Le problème de sécurité, catalogué comme CWE-61, est presque aussi vieux : un programme qui ouvre un symlink sans vérifier où il pointe laisse un attaquant le diriger vers un fichier que l'auteur n'avait jamais visé.

Dans GhostApproval, un dépôt malveillant embarque un symlink au nom innocent, disons project_settings.json. Au lieu de réglages, il contient un pointeur vers ~/.ssh/authorized_keys, ~/.zshrc ou ~/.aws/credentials, dans le répertoire personnel du développeur, bien en dehors du dossier du dépôt. Un README, ou l'utilisateur suivant les instructions du dépôt, demande alors à l'agent de « mettre à jour project_settings.json ». L'agent suit le panneau sans résoudre où il mène, et écrit dans la vraie cible à la place.

WORKSPACE — le dépôt clonéproject_settings.jsona l'air d'un fichier de config — c'est un symlink« Mettez à jour project_settings.json »— le README du dépôt, obéi par l'agentFRONTIÈRE DU WORKSPACEprésumée sûre — la présomption est le bugHOME DU DÉVELOPPEUR (~) — hors du dépôt~/.ssh/authorized_keys~/.zshrc~/.aws/credentialsaccès hôte réel · vraies clés cloud · shell lancé à chaque loginl'agent suit le lienCWE-451 — L'INVITE DÉGUISE CE QUI VA SE PRODUIREVraie cible de l'agent : ~/.ssh/authorized_keysAffiché : « Éditer project_settings.json ? [Oui] »
Comment fonctionne GhostApproval. Un dépôt cloné contient un symlink déguisé en fichier de config ; il pointe hors du workspace, vers le répertoire personnel du développeur. Quand l'agent l'édite, l'écriture atterrit sur ~/.ssh/authorized_keys, ~/.zshrc ou ~/.aws/credentials. Pire, dans plusieurs produits, l'invite de confirmation affichait le nom inoffensif que l'agent devait éditer, pas le chemin sensible qu'il s'apprêtait à toucher (CWE-451).

Deux choses en font davantage qu'une curiosité. La première est ce que font les cibles. Ajouter une ligne à ~/.ssh/authorized_keys installe la clé d'un attaquant pour du SSH sans mot de passe vers votre machine. Écrire dans ~/.zshrc exécute la commande de l'attaquant chaque fois que vous ouvrez un terminal. L'agent d'Augment n'avait même pas besoin d'écrire : interrogé sur « les clés AWS du projet », il a suivi un symlink jusqu'à un fichier d'identifiants et a imprimé les secrets dans le chat, sans la moindre invite.

La seconde est une défaillance plus subtile que Wiz classe comme CWE-451, présentation trompeuse de l'interface utilisateur. Avec Claude Code, le raisonnement interne de l'agent reconnaissait bien le fichier comme une config shell, et le dialogue de confirmation demandait pourtant seulement « Appliquer cette modification à project_settings.json ? ». Le seul moment où un humain aurait pu attraper le danger montrait à l'humain la mauvaise information. Windsurf et Amazon Q Developer faisaient pire : ils écrivaient le fichier sur le disque avant d'afficher les boutons d'approbation, si bien que le dialogue était une invite d'annulation plutôt qu'une barrière. La clé SSH de l'attaquant était déjà en place.

La plupart des éditeurs ont depuis corrigé leur variante : Cursor dans la v3.0 (CVE-2026-50549), Amazon Q dans le language-server 1.69.0 (CVE-2026-12958), Google dans Antigravity v1.19.6, Claude Code avec un avertissement à la résolution des symlinks. C'est la bonne réponse, et vous devriez prendre les mises à jour. Mais corriger la gestion des symlinks de chaque produit répare le mécanisme et laisse l'enjeu là où il était.

L'enjeu se fixe là où se tient l'agent.

Chacune de ces charges suppose la même chose : qu'atteindre le répertoire personnel du développeur vaut le déplacement. Que ~/.ssh contient une clé qui ouvre une vraie machine, que ~/.aws/credentials contient un token cloud actif, et que ~/.zshrc s'exécute dans un shell que vous ouvrirez demain. Le symlink est la route. La destination est un portable qui porte toute votre vie de travail.

La plupart des attaques contre les agents de codage reposent sur la même hypothèse. L'agent est un programme capable et ouvert qui lit des entrées non fiables (dépôts, pages web, archives de packages, sorties d'outils) et agit sur une machine où vos identifiants dorment dans des fichiers en clair, à quelques répertoires de là. Durcissez l'agent tant que vous voudrez ; il partage un système de fichiers et un trousseau avec tout ce que vous exécutez d'autre. Les attaquants continueront de paver de nouvelles routes. Le problème, c'est la destination.

Bromure Agentic Coding supprime la destination. L'agent, les shells qu'il engendre, les dépôts qu'il clone et tout code qu'ils contiennent tournent dans une VM Linux par profil : une machine séparée que l'hyperviseur isole de votre Mac. Vos vrais identifiants n'y entrent jamais. Nous avons expliqué pourquoi une frontière vaut mieux qu'une cage dans pourquoi Bromure Agentic Coding n'est pas une sandbox ; GhostApproval est un test propre de l'idée, parce qu'il s'échappe du dossier du workspace, la seule petite frontière sur laquelle l'industrie s'appuyait.

L'agent sur votre MacUNE MACHINE, UN SEUL HOMEagent + dépôt clonésymlink → ~/.ssh/authorized_keys~/.ssh/authorized_keysRÉEL~/.aws/credentialsRÉEL~/.zshrcRÉELRésultatclé SSH de l'attaquant installée,clés cloud lues, shell détourné —sur la machine où vous travaillez.Bromure Agentic CodingVM PAR PROFIL — JETABLEagent + dépôt clonésymlink → ~/.ssh/authorized_keysl'écriture a bien lieu~/.ssh/authorized_keysSTUB~/.aws/credentialsSTUB~/.zshrcinvitéRésultatune clé ajoutée à un invité jetable,des clés cloud factices exfiltrées —votre Mac n'est jamais entré dans la pièce.
La même évasion par symlink, deux machines. Dans une installation classique, l'agent tourne à côté de vos vrais ~/.ssh et ~/.aws, si bien que suivre le lien atteint des clés actives et une machine qui vaut d'être prise. Dans Bromure, l'agent tourne dans une VM par profil ; le symlink se résout toujours et l'écriture a toujours lieu, mais elle atterrit dans un invité jetable dont le répertoire personnel ne contient que des stubs : des fichiers substituts qui ne signifient rien sur l'internet public. Le système de fichiers de votre Mac se trouve sous la ligne de l'hyperviseur et n'est jamais monté dans la VM.

Suivez la même évasion à l'intérieur de la VM. Le symlink se résout toujours (Bromure n'a jamais besoin de détecter ni de bloquer l'astuce) et l'écriture atterrit toujours sur ~/.ssh/authorized_keys. Mais ce fichier vit dans le répertoire personnel de l'invité, sur une machine jetable qui n'a aucune route vers votre Mac. La clé de l'attaquant accorde désormais l'accès à une machine que vous jetterez, une machine qui n'a de toute façon jamais été joignable de l'extérieur.

La variante vol d'identifiants est encore plus décevante. Quand un agent compromis suit un symlink jusqu'à ~/.aws/credentials et en imprime le contenu dans le chat, il lit un stub : un fichier d'identifiants bien formé dont les clés n'authentifient rien. Les vrais tokens restent sur l'hôte, derrière un broker qui échange le substitut contre le vrai secret seulement au moment d'un appel sortant que vous avez autorisé, et le retire de la réponse. Une dépendance ou un agent piégé qui ratisse le système de fichiers à la recherche de secrets trouve du mobilier.

L'invite, et le second filet.

Bromure répond aux deux angles les plus tranchants de GhostApproval à des couches différentes.

Le premier est le problème de l'invite de confirmation, CWE-451 : le dialogue qui disait project_settings.json alors que l'agent savait qu'il s'agissait de ~/.zshrc. Bromure abandonne le dialogue d'écriture de fichier ; les écritures à l'intérieur de la VM jetable touchent un disque sacrifiable et n'exigent aucune approbation. Bromure insère l'humain à la vraie frontière à la place, pour les appels modifiant l'état qui quittent la VM : un git push, un DROP de base de données, un Terminate cloud. Ceux-là se mettent en pause sur l'hôte et vous montrent l'opération littérale plutôt qu'un résumé écrit par l'agent. C'est le principe même que GhostApproval violait, appliqué là où il compte : le moment où une décision a des conséquences hors de la sandbox est le moment où un humain doit voir le vrai appel.

Le second angle est l'instruction qui lance toute l'attaque : un README disant à l'agent de « mettre à jour project_settings.json », que le modèle lit comme du contexte et suit comme un ordre. Bromure lit ce contenu non fiable en premier, sur l'appareil : un classifieur local évalue les fichiers entrants, les récupérations web et les sorties d'outils à la recherche d'instructions qui n'ont rien à faire là, et les fichiers de règles qu'un agent traite comme des consignes permanentes passent un contrôle plus sévère. Le classifieur ratera certaines formulations, et l'isolation défait cette attaque à elle seule ; considérez-le comme un second filet sous le premier.

Là où la ligne ne vous sauve pas.

L'isolation est une forme précise, et cette forme a des bords.

Un profil vit longtemps, donc la persistance persiste.

La charge ~/.zshrc s'exécutera à la prochaine ouverture d'un shell dans la VM de ce profil. Elle s'exécute dans un invité sans clés de l'hôte et avec de simples identifiants stub, un shell dans une pièce vide, mais elle s'exécute bel et bien. Si cela vous inquiète pour une tâche donnée, lancez-la dans un profil neuf.

Ce que vous montez, vous l'exposez.

La VM ne contient rien de vous par défaut. Si vous montez délibérément un vrai répertoire de l'hôte dans un profil, ou collez un token actif dans l'invité, une évasion par symlink peut l'atteindre. La ligne garde vos secrets dehors ; les remettre dedans est un choix que vous seul pouvez faire.

Délimitez le broker à dessein.

Les stubs signifient que le fichier sur le disque ne vaut rien, mais le broker détient toujours de vrais tokens pour les appels que vous autorisez. Un profil qui n'a besoin que de lire un dépôt ne devrait pas porter un token capable de le publier. L'isolation contient l'explosion ; la délimitation décide de sa taille maximale.

Le workspace était le mauvais mur.

GhostApproval est, au fond, une histoire sur ce que l'industrie a accepté de traiter comme une frontière : le dossier du workspace, l'idée que « l'agent reste dans le répertoire du projet » serait une propriété de sécurité plutôt qu'un comportement par défaut que n'importe quel panneau indicateur peut contourner. Le symlink lui-même est un bug que les éditeurs peuvent corriger, et qu'ils ont corrigé. Tracez votre frontière au niveau d'un dossier et vous passerez l'éternité à prouver qu'aucun symlink, aucun .., aucun chemin astucieux n'en sort jamais. Tracez-la au niveau de l'hyperviseur et la question cesse d'importer, parce que l'endroit vers lequel l'agent s'échappe est une machine jetable qui n'a jamais rien contenu de vous.

Prenez les correctifs des éditeurs ; ils ferment un vrai trou. Mais le trou faisait mal parce que l'agent se tenait sur votre portable, à un répertoire de vos clés. Déplacez l'agent hors de votre portable et la prochaine évasion (il y en aura une) arrive dans une pièce vide. Bromure Agentic Coding est gratuit, open source, et disponible dès aujourd'hui.