Mon agent IA a lancé un curl que je ne lui avais pas autorisé
J’ai monté un petit agent de recherche pour me mâcher la veille. Un orchestrateur qui découpe une question en angles, lance cinq sous-agents en parallèle pour fouiller le web, recoupe les sources, et écrit un rapport cité. Rien d’exotique, c’est le motif classique d’un système multi-agents. Dans le fichier de chaque sous-agent web, j’avais écrit noir sur blanc une règle de sécurité: tu n’as que des outils de recherche, pas de shell, et tu ne touches jamais à mes notes privées.
Puis je l’ai audité, par réflexe. Et dans les journaux d’exécution, je suis tombé sur une ligne que je n’avais jamais autorisée. Un de mes sous-agents web avait lancé un curl en ligne de commande pour aller chercher une page que l’outil de récupération standard n’arrivait pas à lire. Rien de malveillant, le worker voulait juste finir son travail proprement. Mais ça m’a arrêté net, parce que ça cassait une promesse que je croyais tenue. Ma règle « pas de shell » existait. Sauf qu’elle vivait dans un prompt. Et un prompt, ça se contourne.
La vraie question est devenue celle-là: ma séparation des rôles était-elle un contrôle, ou juste une consigne polie que l’agent suivait quand ça l’arrangeait?
Regarder ce que les agents ont vraiment fait
Avant de réparer quoi que ce soit, je voulais des preuves, pas mon ressenti. L’agent tournait dans Claude Code, qui écrit un fichier de transcription par sous-agent, en JSONL, sur le disque. Chaque appel d’outil y est enregistré, avec sa commande, son résultat et l’horodatage. C’est déjà une bonne nouvelle pour un réflexe d’auditeur: il y a une trace, et elle n’est pas générée par moi qui raconte.
Premier piège, et il est instructif. Mon premier grep brut affichait « obsidian: 500 » dans les transcrits. Cinq cents accès à mes notes? Panique courte. En réalité, c’était la liste des outils disponibles présente dans le contexte de chaque agent, pas cinq cents appels. La leçon est bête mais structurante: un comptage de surface mélange « l’outil est dans le menu » et « l’outil a été utilisé ». Il faut parser les événements d’appel réels, pas chercher un mot. Une fois le parsing fait proprement, le verdict est tombé: zéro accès à mes sources internes sur l’ensemble du run, mais deux workers avaient bel et bien appelé un shell pour faire du curl.
Concrètement, le grep qui ment, puis le comptage qui dit vrai:
|
|
Donc la frontière a tenu, mais par discipline de l’agent, pas par contrainte du système. C’est exactement le genre d’écart qui m’inquiète, parce qu’il s’appuie sur la bonne volonté d’un composant qu’un attaquant cherche précisément à manipuler.
Pour cadrer pourquoi ce détail compte, le meilleur point d’entrée que j’ai trouvé est la « lethal trifecta » formulée par Simon Willison en juin 2025. Sa thèse: un agent devient dangereux quand trois capacités se retrouvent réunies. L’accès à des données privées, l’exposition à du contenu non fiable, et un canal de communication vers l’extérieur. Réunissez les trois dans le même agent et une injection cachée dans une page web peut lui faire lire vos données et les renvoyer dehors, sans aucune faille de code au sens classique. Mon worker web cumulait déjà deux des trois pattes: il lisait du contenu non fiable, et avec son curl il avait un canal de sortie. Il lui manquait l’accès aux données privées. La marge était mince.
Le modèle décide, le harnais agit
Pour comprendre où placer un garde-fou, il faut distinguer deux choses qu’on confond souvent. Le modèle, c’est le cerveau. Il réfléchit et demande des actions, mais il ne sait produire que du texte. Le harnais, ou runtime, c’est le programme autour du modèle qui exécute réellement ces actions, applique les permissions et écrit les journaux. Le modèle est un chef qui dicte la recette, le harnais est le commis qui a les couteaux et le feu et qui tient le cahier de ce qui a été fait.
Cette distinction n’est pas un détail de vocabulaire, elle décide de toute la stratégie. Une injection de prompt manipule le chef. Les garde-fous qui tiennent sont donc ceux que le commis applique, pas ceux qu’on espère voir le chef respecter. C’est pour ça que ma règle écrite dans le prompt ne valait rien face au curl: elle s’adressait au chef.
Du mou au dur: l’allowlist d’outils
La vraie correction n’est pas un meilleur prompt, c’est une liste blanche appliquée par le harnais. Dans Claude Code, un sous-agent se définit dans un fichier .claude/agents/<nom>.md avec un en-tête, et ce qui m’avait échappé, c’est qu’on peut y poser un champ tools: qui énumère les outils autorisés. La documentation officielle est explicite sur le point qui m’a piégé: si on omet ce champ, le sous-agent hérite de tous les outils, y compris le shell. C’est exactement ce qui s’était passé. Mon worker n’avait pas reçu de Bash par erreur, il l’avait reçu par défaut, faute que je l’aie cadré.
En fixant tools: WebSearch, WebFetch, le harnais retire Bash du menu de l’agent. Le curl devient impossible, pas parce que l’agent décide d’obéir, mais parce que l’outil n’existe plus pour lui. On passe d’un moindre privilège écrit à un moindre privilège appliqué. C’est le même réflexe que de décider, dans un rapport, qui a le droit de voir quelles lignes: on ne fait pas confiance à la bonne volonté du lecteur, on cadre l’accès en amont.
Concrètement, voici la bascule. Avant, ma règle de sécurité vivait dans le corps du prompt, sans rien pour la faire respecter:
|
|
Après, la même intention passe dans l’en-tête de l’agent, là où le harnais la lit et l’applique:
|
|
La phrase « pas de shell » n’est plus une consigne, c’est l’absence de Bash dans le champ tools. Le harnais ne propose tout simplement plus le shell à cet agent, donc le curl n’a plus d’existence possible pour lui.
Sauf que je me méfie des promesses propres, alors j’ai creusé. Et là, deuxième surprise honnête: il existe une issue ouverte sur le SDK signalant que ces listes d’outils ne sont pas toujours appliquées aux process enfants des sous-agents. Autrement dit, même le mécanisme « dur » a ses zones où il faut vérifier qu’il mord vraiment. Je ne me fie donc pas à la doc, je teste l’application réelle sur ma machine avant de m’appuyer dessus. C’est inconfortable, mais c’est la posture honnête.
Ce que je décris là porte un nom côté menaces. C’est l’Excessive Agency, classée LLM06 dans l’OWASP Top 10 for LLM Applications 2025: des actions dommageables qui découlent d’un excès de fonctionnalités, de permissions ou d’autonomie accordées au système. La même liste OWASP place d’ailleurs le Prompt Injection en tête, LLM01, et depuis fin 2025 il existe un Top 10 dédié aux applications agentiques. Le sujet sort du laboratoire.
Les logs, et pourquoi une injection ne les efface pas
Un point m’a rassuré, et il découle directement de la distinction chef/commis. Les journaux sont écrits par le harnais, pas par le modèle. Une injection ne peut donc pas décider de « ne pas être journalisée », parce que toute action passe par un appel d’outil, et tout appel d’outil traverse la couche qui journalise avant de s’exécuter. Le modèle peut mentir dans son récit, les logs le contredisent, et c’est précisément ce qui en fait une preuve.
Le risque résiduel n’est pas la suppression de la journalisation, c’est sa falsification après coup: faire effacer les fichiers de log, à condition que l’agent ait un accès fichier qui les atteint. On retombe sur la même solution. Retirer le shell au worker ne fait pas que bloquer le curl, ça protège aussi le journal d’audit. Et pour un vrai durcissement, on stocke les logs en ajout seul, idéalement hors de la machine, pour qu’un hôte compromis ne puisse pas réécrire son propre cahier.
Rien de nouveau sous le soleil
C’est le moment où mon bagage me sert le plus, pas pour étaler des outils, mais pour reconnaître un motif. Tout ce que je viens de décrire, ce ne sont pas des inventions de l’ère des agents. C’est l’application de contrôles de sécurité vieux de vingt ans à un objet nouveau.
Côté ISO/IEC 27001:2022, mon cloisonnement et mon allowlist tombent pile sur trois mesures de l’Annexe A: la 8.2 Droits d’accès à privilèges, la 8.3 Restriction d’accès à l’information, et la 5.3 Séparation des tâches. Mes journaux relèvent de la 8.15 Journalisation, qui a une subtilité utile: dans la version 2022, la protection des journaux n’est plus une mesure séparée, elle a été fusionnée dans la 8.15, qui exige donc à la fois de produire les logs et de garantir qu’un utilisateur, même à privilèges, ne puisse pas effacer les siens. Quant à surveiller le comportement de l’agent, c’est la 8.16 Activités de surveillance, l’une des mesures nouvelles de l’édition 2022. Je n’ai rien inventé, j’ai juste relu mon agent avec la grille.
Le NIST dit la même chose, en plus frontal. Le contrôle AC-6 Least Privilege de SP 800-53 applique explicitement le moindre privilège aux « utilisateurs ou aux processus agissant pour leur compte ». Un agent IA est exactement ça: un processus qui agit pour mon compte. Le renforcement AC-6(9) demande même de journaliser l’usage des fonctions privilégiées, et la famille AU couvre la traçabilité et la protection des enregistrements d’audit. Le vocabulaire pour gouverner mon agent existait avant qu’on parle d’agents.
ISO/IEC 42001:2023, la norme de management de l’IA, ajoute la couche qui manquait. C’est un référentiel certifiable, à distinguer du NIST AI RMF qui reste un cadre volontaire et de l’AI Act qui est une réglementation. Son Annexe A, qui est normative, contient une mesure A.9.2 sur l’utilisation responsable des systèmes d’IA, incluant les processus permettant à un humain de revoir et d’outrepasser les décisions. C’est exactement ma règle « aucune action à effet de bord sans validation ». La mesure A.6.2.6 porte l’exploitation et la surveillance du système, et la A.6.2.8 l’enregistrement des journaux d’événements. La même logique, mais nommée pour l’IA.
Et pour rester sur mon terrain, l’ANSSI ne dit pas autre chose. Son guide ANSSI-PA-102 du 29 avril 2024 recommande le cloisonnement, le moindre privilège sur les outils accessibles à l’agent, et une validation humaine dès qu’une action à effet de bord est envisagée. Mes trois réflexes, dans un document français et public.
Ce que ça change, et pour qui
Ce n’est pas un sujet de laboratoire. C’est le quotidien qui arrive pour la PME qui branche un assistant IA sur ses fichiers, le RSSI qui voit débarquer un Copilot, l’intégrateur qui déploie un agent chez un client. Le réflexe à installer tient en quatre décisions, et aucune n’est magique. Pour chaque agent, lister ce qu’il a le droit de toucher au lieu de le laisser tout hériter. Séparer ce qui voit des données privées de ce qui lit du contenu non fiable. Exiger une main humaine sur toute action irréversible. Et garder des journaux que l’agent ne peut pas réécrire.
Un sous-traitant industriel de la vallée de l’Arve qui colle un agent sur ses cahiers des charges n’a pas besoin d’un SOC pour ça. Il a besoin de cadrer les droits de son agent comme il cadrerait ceux d’un prestataire qu’il fait entrer dans ses locaux. C’est de la gouvernance, pas de la sorcellerie. Et c’est là que le décalage me frappe: l’outil est nouveau, les questions à se poser sont anciennes, mais presque personne ne fait le lien parce que le mot « IA » brouille le réflexe sécurité.
Enfin…
Je ne vais pas conclure proprement, parce que plusieurs choses ne le sont pas.
L’allowlist elle-même, mon contrôle « dur », n’est pas toujours appliquée selon les runtimes, l’issue que j’ai citée le montre. Donc le durcissement se vérifie au cas par cas, il ne se proclame pas. Ensuite, même un worker réduit à WebFetch garde un canal de sortie minuscule, puisque récupérer une URL, c’est aussi un moyen d’emporter de l’information dans son adresse. Le durcissement réduit la surface, il ne la supprime pas, et la vraie défense reste en couches: ne jamais mettre de secret dans le contexte d’un agent exposé.
Plus profond, la gouvernance de l’identité d’un agent est immature. ISO parle de gestion des identités, mais qui « est » mon agent, avec quels droits, révoqués quand, audités par qui? Je n’ai pas de réponse propre, et je ne crois pas que le secteur en ait une non plus. Les cadres agentiques sont tout récents, l’OWASP dédié date de fin 2025, le NIST s’y met à peine. On écrit la doctrine en marchant, et c’est précisément pour ça que noter ses propres ratés, comme ce curl, a de la valeur.
La veille à suivre
Je continue à creuser quelques fils. L’application réelle des listes blanches d’outils selon les harnais, parce que c’est le maillon dont dépend tout le reste. Le Top 10 OWASP pour applications agentiques et la taxonomie NIST des attaques sur l’IA générative, pour voir si ma grille tient face à des scénarios plus retors que mon curl bon enfant. Les premiers retours de terrain sur la certification ISO 42001, qui dira si la norme est opérationnelle ou seulement élégante. Et la question de l’identité des agents dans la durée, qui me paraît être le prochain vrai chantier de gouvernance.
Sources principales: la lethal trifecta de Simon Willison ; OWASP Top 10 for LLM Applications 2025 ; documentation des sous-agents Claude Code ; NIST SP 800-53 AC-6 ; ISO 42001 expliquée ; recommandations ANSSI pour l’IA générative. Les numéros de mesures ISO 27001:2022 et 42001:2023 cités proviennent de sources secondaires concordantes, le texte intégral des normes étant payant.