--allowedTools ne limite rien : mon agent Claude exécutait Bash sans rien demander (le correctif tient en 2 drapeaux)

Sommaire (7 parties)

Mon site fait tourner de petits scripts. Ils confient du texte écrit par des inconnus à un agent, un programme qui s'appuie sur un modèle d'IA et peut agir. Le mien est Claude Code, lancé par claude -p : une question, une réponse, pas d'interface. Un commentaire LinkedIn auquel un bot répond, une page web qu'on résume. Je voulais du texte en retour, jamais un shell, c'est-à-dire une invite de commandes. Si le texte d'un inconnu dit « lance telle commande », l'agent ne doit pas pouvoir obéir. C'est une injection de prompt.

Le 17 août 2026, j'ai corrigé cinq de ces appels d'un seul coup. Deux d'entre eux montrent le problème.

  • Dans le premier, j'avais écrit --allowedTools 'WebSearch,WebFetch', persuadé que l'agent n'aurait que ces deux outils.
  • Dans celui du bot LinkedIn, je n'avais mis aucun drapeau. Ma mesure d'août (version 2.1.233) comptait 30 outils, dont Bash, qui lance des commandes.

Ce billet explique pourquoi --allowedTools, que je prenais pour une limite, ne limite rien. J'ai tout remesuré aujourd'hui sur une version plus récente, la 2.1.319, et les totaux ne sont plus ceux d'août.

Deux questions que le nom du drapeau mélange

Un outil est une action qu'un agent peut déclencher : lire un fichier, lancer une commande (l'outil Bash), chercher sur le web. Un drapeau est une option qu'on ajoute à la commande, comme --tools. Deux questions se posent à propos d'un outil. Le modèle voit-il l'outil ? Et, s'il l'utilise, quelqu'un doit-il confirmer avant ?

--allowedTools répond à la seconde. La référence CLI le dit ainsi : ce sont les outils qui s'exécutent « without prompting for permission » (sans demander la permission). Pour limiter les outils proposés au modèle, il faut utiliser --tools. Le mot « allowed » se lit comme une liste d'outils retenus, alors que c'est une dispense de confirmation.

En mode -p, personne ne peut répondre à une demande de confirmation. Claude Code applique alors ses règles de permission. Les miennes autorisent Bash : quand le modèle voit cet outil, il s'exécute sans qu'on lui demande rien.

Mesurer la liste d'outils, sans demander au modèle

Demander au modèle la liste de ses outils ne prouve rien : il peut l'inventer. La vraie liste est dans le premier événement du flux stream-json, celui de type init.

claude -p "Réponds seulement : ok" --tools '' \
  --output-format stream-json --verbose | grep -m1 '"subtype":"init"'

La ligne obtenue contient le champ tools. C'est la liste d'outils que Claude Code présente au modèle au démarrage : le tableau l'appelle « annoncés ». Voici ce que j'ai compté sur ma machine, version 2.1.319. Les 33 outils MCP viennent de mon poste, ton total sera différent. MCP (Model Context Protocol) désigne les outils fournis par des extensions, ici Claude Docs et Playwright, face aux outils natifs comme Bash.

Drapeaux Outils annoncés Bash, Write, Edit dont MCP
(aucun drapeau)61oui33
--allowedTools 'WebSearch,WebFetch'61oui33
--tools 'WebSearch,WebFetch'35non33
--tools ''33non33
--tools 'WebSearch,WebFetch' --strict-mcp-config2non0
--tools 'WebSearch,WebFetch' --disallowedTools 'mcp__*'2non0
--tools '' --strict-mcp-config0non0

Pour lire les lignes du tableau :

  • Write et Edit sont les outils qui écrivent des fichiers.
  • --disallowedTools refuse des outils nommés.
  • mcp__* désigne tous les outils MCP.

Premier constat : mes deux outils autorisés donnent le même résultat que sans drapeau, 61. --allowedTools ne change donc pas la liste, c'est la liste par défaut.

Deuxième constat, que la documentation signale aussi : --tools retire les outils natifs mais ne touche pas aux outils MCP. D'où la règle : deux verrous sur la liste, --tools pour les outils natifs et --strict-mcp-config pour les outils MCP. Ce dernier drapeau dit à Claude Code de ne charger que les serveurs MCP indiqués en ligne de commande.

Annoncé, donc appelable : j'ai appelé

Une liste d'outils annoncés prouve peu. Cette fois je ne lui demande pas ce qu'il a, je lui demande de s'en servir. Je lis dans le flux que l'outil a vraiment tourné. Deux appels :

  • Avec --allowedTools 'WebSearch,WebFetch', l'agent a lancé echo PREUVE_BASH_ok avec Bash, sans aucune demande, et m'a répété la sortie.
  • Avec --tools '', il a appelé un outil MCP en lecture seule : le guide de l'extension Claude Docs. Il m'a cité la première ligne reçue.

Je n'ai pas mesuré la probabilité qu'une injection de prompt (un texte piégé qui donne des ordres à l'agent) le convainque de s'en servir. Ce n'est pas le sujet : une défense qui repose sur la bonne volonté du modèle n'en est pas une.

La correction : --tools '' et --strict-mcp-config, zéro outil

Pour un script qui ne veut que du texte en retour, voici l'appel du bot LinkedIn tel qu'il tourne depuis août. --print est le nom long de -p. Le texte du commentaire part dans le prompt, donc il est écrit par un inconnu.

const { stdout } = await execFile('claude',
  ['--print', '--tools', '', '--strict-mcp-config', prompt]);

La mesure donne 0 outil. Si l'agent doit chercher sur le web, --tools 'WebSearch,WebFetch' avec --strict-mcp-config donne exactement deux outils. --disallowedTools 'mcp__*' retire aussi les outils MCP et marche tout aussi bien. Je préfère --tools (les outils retenus) à --disallowedTools (les outils refusés) : un outil ajouté demain n'entre pas tout seul dans la liste.

Ce que le classement OWASP en dit

L'OWASP est une association qui classe les risques de sécurité. Son Top 10 vise les applications LLM, c'est-à-dire les agents qui s'appuient sur un modèle de langage. Une édition 2026 existe : la page officielle porte la date du 3 août 2026. L'autonomie excessive (Excessive Agency) désigne un agent qui a plus de pouvoirs que nécessaire. D'après le tableau de la Cloud Security Alliance, ce risque passe de la sixième à la troisième place. Je n'ai pas lu la liste sur la page officielle, seulement ce tableau.

Mon agent correspond exactement à cette définition. Ce n'est pourtant pas une découverte. La documentation précise que --tools, et non --allowedTools, limite les outils. Une faille publique, CVE-2026-39398, en donne un cas réel. Un paquet annonçait un bac à sable (un espace où l'agent ne peut rien casser) avec --allowed-tools "". C'est le même drapeau, écrit avec des tirets. Pourtant tous les outils restaient disponibles. Ce que j'apporte : des chiffres sur une version précise, et une recette que j'ai vue donner zéro.

Conclusion

Mon avis : un drapeau qui s'appelle « allowed » est un piège de nommage. La documentation ne suffit pas à le désamorcer : j'avais lu l'aide et fait l'erreur quand même. Je ne fais plus confiance à ce que le drapeau dit. Je lis la liste annoncée au démarrage, et un test qui échoue si Bash y figure serait la bonne suite.

Ce que je n'ai pas mesuré : le taux de réussite d'une injection, les autres versions, les modes de permission sans aucune confirmation. Je n'ai pas non plus testé un poste sans mes réglages.

Sources

Documentation consultée le 9 novembre 2026 :

Dans le grand livre du développeur

Un livre gratuit où chaque idée est racontée une seule fois, à sa place, du silicium au jugement humain.

Commentaires (0)