Tutoriel

Isoler un agent de code IA sur un VPS : AGENTS.md piégé

Intelligence Artificielle12 min de lecture9 étapes

Le 25 octobre 2026, BleepingComputer a rapporté qu'un `AGENTS.md` piégé, chargé automatiquement quand un agent est lancé dans le mauvais dossier, a suffi à faire exécuter des commandes à Codex. Pour une agence, le risque se mesure à ce que l'agent peut toucher : clés de déploiement, jetons, dépôts d'autres clients. Ce guide monte un VPS de développement dédié, avec un utilisateur sans privilège, un répertoire de projet étroit, aucun secret de production et une sortie réseau restreinte, puis une revue des fichiers d'instructions avant chaque session.

Sommaire· Ce que montre la recherche de Forever Security1/13
  1. 01Ce que montre la recherche de Forever Security
  2. 02Ce que l'étude établit, et ce qu'elle ne dit pas
  3. 03Lancer l'agent dans le bon dossier et valider les commandes : pourquoi un serveur séparé ?
  4. 04Prérequis : ce qu'il faut pour monter le VPS
  5. 05Monter le VPS en neuf étapes
  6. 06Chaque garde-fou borne une chose précise
  7. 07Relire les fichiers d'instructions avant qu'un agent les suive
  8. 08Le cas du paquet function-color : ce que le filtre change, et ce qu'il ne change pas
  9. 09Compter les refus, et couper les scripts d'installation
  10. 10Dépannage : les erreurs que vous allez rencontrer
  11. 11Ce que ce serveur ne règle pas
  12. 12Pour une agence : un VPS par client
  13. 13La documentation officielle

Ce que montre la recherche de Forever Security

Le 25 octobre 2026, BleepingComputer a rapporté les travaux de Forever Security. Les chercheurs ont trouvé, dans le dossier ~/Downloads d'un client, un fichier AGENTS.md piégé qui demandait à un agent de code d'installer un paquet npm nommé « function-color ». Ce dossier était aussi le répertoire de travail de l'agent, et les agents chargent automatiquement les fichiers d'instructions de leur répertoire de travail. Scénario reconstitué : une page web dépose le fichier dans les téléchargements, l'utilisateur lance son agent dans ce dossier, l'agent suit les instructions avec les droits qu'il possède déjà.

Sur Codex d'OpenAI, la chaîne complète, avec une charge inoffensive (ouvrir la calculatrice), a réussi dans 90 % des tests. Avec Claude Code, un CLAUDE.md piégé redirigeait le trafic de l'API vers un serveur des chercheurs, ce qui exposait le jeton OAuth et permettait d'exécuter du code dans environ 50 % des tests. Les chercheurs estiment qu'il n'y a rien à corriger côté OpenAI ou Anthropic : l'agent fonctionne comme prévu.

Ce que l'étude établit, et ce qu'elle ne dit pas

  • Établi — un fichier d'instructions placé dans le répertoire de travail suffit à orienter l'agent, sans qu'aucune faille logicielle soit exploitée.
  • Condition — l'attaque exige que l'utilisateur lance l'agent dans le dossier concerné. Selon BleepingComputer, OpenAI et Anthropic recommandent d'ailleurs de lancer l'agent dans le répertoire du projet.
  • Charges inoffensives — les taux de 90 % et d'environ 50 % viennent de tests avec une charge sans danger ; ils ne mesurent pas des attaques réelles et ne se généralisent pas à tous les outils.
  • Livraison inconnue — les chercheurs n'ont pas pu déterminer comment le fichier du client est arrivé dans ses téléchargements : la page web reste une hypothèse.
  • Cursor CLI — cité comme lancé depuis ce dossier, mais pas décrit comme testé.
  • Notre lecture — un dépôt cloné peut contenir son propre fichier d'instructions. Ce point est une déduction de notre part, pas un résultat de l'étude.

Lancer l'agent dans le bon dossier et valider les commandes : pourquoi un serveur séparé ?

La validation humaine et le sandbox de l'outil sont de vrais garde-fous, et il faut les garder activés : l'article source le recommande aussi. Ils reposent pourtant sur une attention constante, sur un outil bien réglé et sur un lancement dans le bon dossier. Le jour où l'un des trois lâche, la question devient : que peut atteindre l'agent détourné ?

Sur un poste de travail, la réponse est longue : clés SSH, jetons cloud, dépôts d'autres clients, navigateur connecté. Sur un VPS dédié, elle est courte et vous la choisissez. Le serveur ne contient que le projet, un jeton à portée minimale et un utilisateur sans privilège. Il se détruit et se recrée sans toucher au poste. Un VPS ne rend pas l'agent sûr : il borne son rayon d'action, c'est la seule promesse de ce guide.

Prérequis : ce qu'il faut pour monter le VPS

Les agents de code appellent l'API d'un fournisseur : le calcul du modèle ne se fait pas sur votre serveur, donc le VPS reste modeste. Prévoyez 2 vCPU, 4 Go de RAM et 40 Go de disque pour un projet Node ou PHP ordinaire, davantage si vous compilez ou lancez des conteneurs. Il faut un accès root, Debian ou Ubuntu LTS, une IPv4 dédiée et une clé SSH pour vous connecter.

Côté réseau, un seul port est ouvert en entrée : le 22, pour SSH. En sortie, l'utilisateur de l'agent ne joint que le port 8888 de la boucle locale, où écoute un proxy qui relaie vers une liste courte d'hôtes. Relevez avant de commencer les hôtes dont votre outil a besoin : registre npm, API du fournisseur, dépôt Git. Les commandes sont écrites pour Debian et Ubuntu (apt, ufw, nftables) et supposent un compte d'administration avec sudo.

Monter le VPS en neuf étapes

  1. Créer un utilisateur sans privilège

    sudo adduser --disabled-password --gecos "" agent
    id agent
    sudo -l -U agent
    La deuxième commande ne doit pas lister le groupe sudo, la troisième doit répondre que l'utilisateur n'est pas autorisé à exécuter sudo. Le compte n'a pas de mot de passe : on y entre par sudo -iu agent depuis votre compte d'administration. Prévoyez un VPS par client.

  2. Réserver un répertoire par projet

    sudo install -d -o agent -g agent -m 700 /srv/agents/projet-a
    Lancez toujours l'agent depuis cette racine, jamais depuis /, ~ ou un dossier de téléchargements : un répertoire parent large charge des fichiers d'instructions que vous n'avez pas relus. Pour récupérer le code, entrez dans la session de l'agent puis clonez :
    sudo -iu agent
    git clone <url-du-dépôt> /srv/agents/projet-a
    Utilisez une clé de déploiement en lecture seule, générée sur cette VM et propre au dépôt du projet.

  3. Couper l'accès à vos clés

    sudo -iu agent printenv SSH_AUTH_SOCK
    echo 'AllowAgentForwarding no' | sudo tee /etc/ssh/sshd_config.d/10-agents.conf
    sudo sshd -t && sudo systemctl reload ssh
    La première commande ne doit rien afficher : aucun agent SSH n'est transmis à la session de l'agent de code. Connectez-vous à la VM avec ssh -o ForwardAgent=no. Ne déposez aucune clé de déploiement de production sur ce serveur, et aucun fichier de configuration d'un autre client.

  4. Limiter les variables d'environnement au projet

    sudo -iu agent
    install -m 600 /dev/null ~/.agent-env
    Éditez ensuite ~/.agent-env pour y mettre uniquement le jeton du fournisseur, à portée minimale, dédié à cette VM et révocable. Lancez l'agent avec un environnement vidé, afin qu'il n'hérite d'aucune variable de votre session :
    env -i HOME="$HOME" PATH="$PATH" TERM="$TERM" HTTPS_PROXY=http://127.0.0.1:8888 bash -c 'set -a; . ~/.agent-env; cd /srv/agents/projet-a && exec claude'
    Remplacez claude par la commande de votre outil. Si l'outil se connecte par compte (OAuth) plutôt que par clé d'API, le jeton vit dans le répertoire personnel de l'utilisateur : c'est ce jeton que l'étude a vu exposé avec Claude Code. Préférez une clé d'API dédiée, que vous révoquez à la destruction du serveur.

  5. Fermer l'entrée avec ufw

    sudo ufw default deny incoming
    sudo ufw limit 22/tcp
    sudo ufw enable
    Gardez une seconde session SSH ouverte. ufw filtre par adresse et par port, pas par nom d'hôte ni par utilisateur : c'est pourquoi la sortie se règle aux deux étapes suivantes, avec un proxy et nftables.

  6. Installer un proxy de sortie à liste blanche

    sudo apt install tinyproxy
    Dans /etc/tinyproxy/tinyproxy.conf, réglez Listen 127.0.0.1, Port 8888, FilterDefaultDeny Yes, Filter "/etc/tinyproxy/allowlist", puis écrivez la liste :
    printf '%s\n' '^registry\.npmjs\.org$' '^api\.anthropic\.com$' | sudo tee /etc/tinyproxy/allowlist
    sudo systemctl restart tinyproxy
    Chaque ligne est une expression régulière sur le nom d'hôte. Remplacez l'API d'exemple par celle de votre fournisseur, relevée dans sa documentation, et ajoutez ^github\.com$ seulement si le projet l'exige. La documentation de tinyproxy décrit un filtrage par domaine, mais ne dit pas qu'il s'applique à l'hôte des tunnels HTTPS (CONNECT) : c'est non documenté, donc à tester. Ne tenez pas ce filtre pour acquis avant le test de l'étape 8.

  7. Interdire à l'agent toute autre sortie avec nftables

    Ajoutez à la fin de /etc/nftables.conf, après la ligne flush ruleset :
    table inet agent_egress {
    chain output {
    type filter hook output priority 0; policy accept;
    meta skuid "agent" oifname "lo" tcp dport 8888 accept
    meta skuid "agent" limit rate 5/minute log prefix "agent-egress-drop: "
    meta skuid "agent" counter drop
    }
    }
    L'utilisateur agent ne peut plus joindre que le proxy local ; le reste est journalisé puis jeté. Contrôlez la syntaxe avant d'activer :
    sudo nft -c -f /etc/nftables.conf
    sudo systemctl enable --now nftables

  8. Tester que le filtre tient

    sudo -u agent curl -sS --max-time 5 https://example.com
    sudo -u agent curl -sS -x http://127.0.0.1:8888 https://example.com
    sudo -u agent curl -sS -x http://127.0.0.1:8888 -o /dev/null -w '%{http_code}\n' https://registry.npmjs.org/-/ping
    La première commande doit expirer, car le pare-feu jette la connexion directe. La deuxième doit échouer par un refus du proxy (avec curl, un message du type CONNECT tunnel failed ; le code exact figure dans le journal de tinyproxy). La troisième doit répondre 200. Ces résultats sont ceux attendus : constatez-les sur votre VM. Si la deuxième commande réussit, le filtre ne couvre pas les tunnels HTTPS : préférez alors un proxy dont la documentation couvre CONNECT, et corrigez avant de lancer l'agent.

  9. Prévoir la destruction du serveur

    Rangez ces commandes dans un script versionné. Un VPS suspect se supprime et se recrée à partir de l'image d'origine, et vous révoquez le jeton du fournisseur. Pour votre propre accès, passez par un bastion : voir le guide lié en bas de page.

Chaque garde-fou borne une chose précise

Faites défiler le tableau

Garde-fouCe qu'il borneCe qu'il laisse passer
Validation humaine des commandesChaque commande exécutéeUne commande d'apparence anodine validée par habitude
Sandbox de l'outilLes actions de l'agent, selon son réglageCe qui dépend d'un réglage oublié ou propre à l'outil
Utilisateur sans privilègeDroits root, fichiers système, autres comptesLes fichiers de l'utilisateur lui-même, dont son jeton
Répertoire de projet étroitLes fichiers d'instructions chargés par erreurLe contenu du projet lui-même
Sortie réseau filtréeUne installation depuis un hôte non prévu, un envoi vers un hôte inconnu (une fois le filtre vérifié)Tout ce qui passe par un hôte autorisé, registre ou API
Aucune clé de production sur la VML'accès aux serveurs de production et aux autres clientsCe que le jeton fournisseur et le dépôt du projet exposent
VPS dédié et recréableLa persistance après compromission, le poste de travailLes données du client présentes sur la VM

Relire les fichiers d'instructions avant qu'un agent les suive

Un fichier d'instructions agit comme du code, par l'intermédiaire de l'agent : relisez-le comme tel. Inventaire : find /srv/agents -type f \( -name AGENTS.md -o -name CLAUDE.md \) -not -path '*/node_modules/*' . Pour détecter un changement, gardez une empreinte :
find /srv/agents -type f \( -name AGENTS.md -o -name CLAUDE.md \) -not -path '*/node_modules/*' -exec sha256sum {} + | sort -k2 > ~/instructions.sha256
puis comparez avant chaque session avec find /srv/agents -type f \( -name AGENTS.md -o -name CLAUDE.md \) -not -path '*/node_modules/*' -exec sha256sum {} + | sort -k2 | diff ~/instructions.sha256 - . Dans un dépôt, git log -p -- AGENTS.md CLAUDE.md montre ce qui a changé depuis votre dernière relecture.

Les outils ne cherchent pas tous leurs fichiers au même endroit (répertoire courant, dossiers parents, dossier personnel) : lisez la documentation du vôtre et gardez ces emplacements vides, ou connus. Ne lancez jamais un agent dans ~/Downloads ni dans un dossier parent large : c'est la première mitigation de l'étude.

Le cas du paquet function-color : ce que le filtre change, et ce qu'il ne change pas

Dans le cas documenté, le fichier piégé demandait d'installer le paquet npm « function-color ». Le compte rendu ne précise pas depuis quel registre. Notre lecture, qui est une mitigation et non un résultat de la recherche : une sortie restreinte à une liste d'hôtes devrait bloquer ou signaler, une fois le filtre vérifié à l'étape 8, l'installation depuis un registre ou un serveur non prévu, et le journal du proxy le montre.

Elle ne bloque pas un paquet publié sur le registre autorisé, puisque registry.npmjs.org est sur la liste. Contre ce cas, comptez sur la relecture du fichier d'instructions, sur une liste de paquets approuvés et, surtout, sur l'absence de secrets à voler. Le guide sur la chaîne d'approvisionnement npm, lié plus bas, détaille cette partie.

Compter les refus, et couper les scripts d'installation

Lisez chaque jour les refus : sudo journalctl -k --since today | grep agent-egress-drop
sudo grep -i filtered /var/log/tinyproxy/tinyproxy.log
Un refus répété vers un hôte inconnu est un signal à comprendre avant d'ajouter l'hôte à la liste : relevez le nom, cherchez pourquoi l'outil l'appelle, puis décidez. Côté npm, npm config set ignore-scripts true (à lancer en tant que agent) coupe l'exécution automatique des scripts d'installation des dépendances.

Dépannage : les erreurs que vous allez rencontrer

Chaque remède ci-dessous évite d'affaiblir le filtre.

  • Un refus de sudo pour agent — comportement attendu : l'utilisateur n'a aucun privilège, et le libellé exact du refus dépend de la version de sudo. Faites l'opération avec votre compte d'administration, sans jamais ajouter agent au groupe sudo.
  • Un échec de tunnel CONNECT (avec curl, un message du type CONNECT tunnel failed) — le proxy a probablement refusé l'hôte ; le code exact dépend de tinyproxy et figure dans son journal. Lisez son nom dans /var/log/tinyproxy/tinyproxy.log, puis ajoutez-le à la liste seulement si l'outil en a besoin.
  • Selon l'outil, un délai dépassé ou un échec réseau (par exemple ETIMEDOUT ou fetch failed) — l'outil tente une connexion directe que nftables jette. Vérifiez que HTTPS_PROXY est exporté dans la session de l'agent. Certains clients ignorent cette variable : ils échouent alors fermés, et leur documentation dit s'ils acceptent un proxy.
  • Permission denied en lisant le dossier d'un autre utilisateur — c'est le cloisonnement qui travaille. Si l'agent a besoin d'un fichier, copiez-le dans le répertoire du projet.
  • Connexion SSH coupée après ufw enable — la règle du port 22 manquait, ou SSH écoute ailleurs. Depuis la console du VPS, lancez sudo ufw status puis sudo ufw allow <port>/tcp.

Ce que ce serveur ne règle pas

Le VPS borne le rayon d'action, il ne supprime pas le risque. Le jeton du fournisseur est sur la machine : un agent détourné peut l'utiliser tant que vous ne l'avez pas révoqué. Le code du client y est aussi, et une fuite reste possible par un hôte autorisé, puisqu'un registre ou un dépôt Git accepte aussi des données en entrée. Ce montage s'ajoute aux invites d'approbation et au sandbox de l'outil, il ne les remplace pas.

Pour une agence : un VPS par client

Le cloisonnement le plus simple à expliquer à un client est physique : un VPS par client, avec son utilisateur, son jeton et son pare-feu. Aucune clé de production et aucun autre client n'y figurent, donc un agent détourné ne dépasse pas ce périmètre. Un seul script de mise en place pour tous les serveurs : une règle qui change se rejoue partout. À la fin d'une mission, vous supprimez le VPS, vous révoquez le jeton et vous passez à la suite sans résidu sur votre poste. Un VPS ServOrbit.com fournit l'accès root, une IPv4 dédiée et le choix de l'OS : c'est la base de ce schéma.

La documentation officielle

Compte rendu de BleepingComputer sur la recherche de Forever Security : https://www.bleepingcomputer.com/news/security/ai-coding-agents-hijacked-using-agentsmd-file-in-downloads-folder/

Documentation nftables : https://wiki.nftables.org/wiki-nftables/index.php/Quick_reference-nftables_in_10_minutes

Documentation de tinyproxy : https://tinyproxy.github.io/

Page de manuel de adduser (Debian) : https://manpages.debian.org/stable/adduser/adduser.8.en.html

Configuration de npm : https://docs.npmjs.com/cli/using-npm/config

Un VPS par client, cloisonné et recréable

Une agence qui isole ses agents de code donne à chaque client son propre VPS : accès root, IPv4 dédiée, choix de l'OS. Un agent détourné reste alors dans le périmètre d'un seul client. L'option administration VPS existe pour qui ne veut pas tenir le serveur ; ce guide, lui, est fourni sans prise en charge de la mise en place par ServOrbit.com.

Besoin d'aide ?

Parcourez notre centre d'aide et notre FAQ, ou contactez notre équipe — rappel, WhatsApp ou e-mail. Support en français, anglais et arabe.

Écrire sur WhatsApps'ouvre dans un nouvel onglet