8 min de lecture

Vos tests de fuite de secrets d’agent prouvent-ils quelque chose ?

Les tests de fuite de secrets d’un agent doivent prouver davantage qu’une conversation propre. Créez des contrôles canaris pour les environnements, les arguments, les journaux, les erreurs et les crashs.

Vos tests de fuite de secrets d’agent prouvent-ils quelque chose ?

Un agent n’a pas besoin d’afficher un jeton pour l’avoir reçu. Si un jeton bearer entre dans son environnement, sa ligne de commande, la charge utile d’un outil, sa transcription, un processus enfant ou un artefact de crash, la frontière a déjà cédé. Une réponse polie du modèle ne corrige pas cette erreur.

Le test à exécuter n’est pas « l’agent a-t-il évité de répéter le secret ? » C’est plutôt : « pouvons-nous faire passer de vraies actions par une passerelle de test, recueillir tous les artefacts que le côté agent peut produire et démontrer qu’aucun ne contient un identifiant fonctionnel unique ? » Cette affirmation est assez précise pour être testée et assez solide pour détecter les défaillances qui apparaissent en production.

Utilisez des comptes jetables, des hôtes jetables et un nouveau canari à chaque exécution. Gardez le secret uniquement dans la configuration de la passerelle. Demandez ensuite à l’agent d’effectuer des opérations HTTP et SSH réussies, déclenchez volontairement des erreurs et inspectez le côté agent comme si vous vous attendiez à une fuite. C’est cette posture qui révèle les chemins les plus problématiques.

Une réponse propre ne prouve pas que la frontière est propre

Un agent peut recevoir un secret sans jamais le placer dans du langage naturel. Une fuite courante vient d’une implémentation d’outil qui demande à l’agent d’appeler lui-même une API, puis place une valeur Authorization dans les entrées de l’outil. Autre possibilité : un lanceur de sous-processus hérite de API_TOKEN parce que personne n’a remplacé son environnement avant l’exécution. Un troisième cas est celui d’un assistant SSH qui écrit un fichier d’identité temporaire à un emplacement lisible par l’agent.

Ces défaillances sont différentes, mais elles ont la même conséquence : le processus de l’agent détient des éléments qui lui permettent d’agir en dehors de la passerelle. Une fois ce point atteint, les demandes d’approbation et les journaux d’audit ne décrivent qu’une partie du risque. L’agent peut copier le secret dans un dépôt, l’envoyer à un autre service ou le laisser dans une transcription que quelqu’un exportera plus tard.

Séparez bien deux affirmations :

  • L’isolation des actions signifie que l’agent demande une opération et reçoit son résultat, tandis qu’un autre composant injecte l’identifiant et réalise l’échange réseau ou SSH.
  • La non-transmission du secret signifie que l’identifiant et ses dérivés utilisables n’entrent jamais dans le processus de l’agent ni dans des fichiers qu’il peut lire.

Les équipes testent souvent la première affirmation et supposent que la seconde est acquise. C’est ainsi qu’un agent peut appeler avec succès une API de test par l’intermédiaire d’une passerelle tout en recevant le jeton dans un champ de débogage ou une variable d’environnement héritée.

La documentation Apple sur les processus est claire concernant l’héritage : un sous-processus reçoit son environnement du processus qui le lance, sauf si le lanceur le modifie avant le démarrage. Ses API exposent également au processus ses arguments de commande et ses données d’environnement. C’est pourquoi un processus enfant n’est pas un simple détail d’implémentation dans ce test. C’est un point d’observation supplémentaire.

Le standard de test devrait être formulé ainsi :

Étant donné un canari d’identifiant inédit, stocké uniquement dans la passerelle, un agent peut effectuer les actions HTTP et SSH prévues, mais aucun processus accessible depuis l’agent, aucune transcription, aucun journal, aucune sortie d’erreur et aucun artefact de diagnostic collecté ne contient le canari ni un encodage identifiable de celui-ci.

Ne prétendez pas qu’un test en boîte noire prouve qu’un secret n’a jamais occupé le moindre octet de mémoire dans la passerelle. Il ne le peut pas. Il établit en revanche la frontière qui intéresse les utilisateurs : l’agent ne reçoit jamais d’identifiant utilisable par les interfaces et les artefacts qu’il contrôle.

Un identifiant de test a besoin d’un usage et d’une empreinte

Un jeton de test doit avoir une seule utilité, et rien de plus. Pour HTTP, créez un compte de test capable d’appeler un seul endpoint, comme GET /whoami ou POST /echo-action, qui renvoie un identifiant de compte inoffensif. Pour SSH, créez un compte limité sur un hôte jetable et autorisez un petit ensemble de commandes qui écrivent un événement côté serveur.

N’utilisez pas un jeton comme test-token pour le rechercher ensuite. Les marqueurs courts et prévisibles créent de faux positifs et rendent les vérifications d’encodage inutiles. Générez une chaîne différente à chaque exécution. Donnez-lui un préfixe reconnaissable, suivi de données aléatoires, afin qu’une personne puisse identifier une défaillance sans confondre une sortie ordinaire avec un secret.

Par exemple, un banc de test peut créer un canari de cette forme :

sallyport_probe_7M3jP4Fqk2rV9dN8xC5a

Cette chaîne est une valeur d’identification, pas un identifiant destiné à être affiché par l’agent. Conservez une étiquette publique distincte pour les journaux, comme run-2026-07-22-ssh-04. L’étiquette peut apparaître dans les transcriptions. Le canari, lui, ne doit jamais y apparaître.

Utilisez des canaris différents pour chaque canal et chaque chemin d’erreur. Réutiliser le même jeton HTTP dans tous les tests transforme une fuite unique en un ensemble de correspondances obsolètes. Il devient aussi difficile de savoir si un artefact ultérieur vient de l’exécution actuelle ou d’un nettoyage précédent qui a échoué.

Un dispositif de test pratique comporte quatre éléments :

  1. Un compte HTTP de test dont le serveur renvoie une réponse fixe et non secrète après une authentification valide.
  2. Un compte SSH de test dont la commande forcée enregistre un identifiant d’action et renvoie un message fixe.
  3. Une entrée d’identifiant dans la passerelle, contenant le canari inédit et aucune copie accessible à l’agent.
  4. Un manifeste situé hors du répertoire des artefacts, qui associe l’étiquette d’exécution aux canaris utilisés pendant cette exécution.

Le manifeste contient des éléments sensibles liés au test. Stockez-le à un endroit illisible par l’agent et supprimez les canaris après l’exécution. Le compte de test doit refuser ces identifiants après le nettoyage, même si une défaillance a laissé une copie dans une archive locale.

Sallyport permet à la passerelle de conserver les identifiants HTTP et SSH de test, tandis que l’agent reçoit les résultats des actions et non les identifiants eux-mêmes.

L’enregistrement côté serveur est important. Il prouve que l’authentification a eu lieu et empêche ainsi un mauvais test de réussir parce que l’action n’a jamais été exécutée. Une réponse comme authenticated action accepted for run-2026-07-22-ssh-04 fournit à l’agent une preuve suffisante de réussite sans renvoyer les données d’authentification.

La capture du lancement détecte la fuite avant le premier appel d’outil

Capturez les arguments initiaux et l’environnement de l’agent à la frontière de lancement. Ce test détecte les secrets transmis par des scripts shell, une configuration CI, des intégrations d’éditeur, des wrappers et des lanceurs pratiques. Il détecte également une erreur fréquente après l’ajout d’une passerelle : laisser l’exportation API_TOKEN en place parce que le nouveau chemin semble fonctionner.

Démarrez l’agent à l’aide d’un wrapper que vous contrôlez. Le wrapper écrit un instantané exact de son propre vecteur d’arguments et de son environnement dans un répertoire de test protégé, puis se remplace par l’exécutable de l’agent. Ce remplacement est important, car il enregistre les valeurs fournies au lancement réel de l’agent, plutôt qu’une reconstruction approximative après le démarrage de plusieurs couches.

Ce petit wrapper Python suffit pour un banc de test local :

#!/usr/bin/env python3
import json
import os
import pathlib
import sys

out = pathlib.Path(os.environ["PROBE_LAUNCH_RECORD"])
out.parent.mkdir(parents=True, exist_ok=True)
record = {
    "argv": sys.argv[1:],
    "environment": dict(os.environ),
}
out.write_text(json.dumps(record, sort_keys=True), encoding="utf-8")
os.execvp(sys.argv[1], sys.argv[1:])

Lancez-le avec un environnement volontairement réduit. N’incluez que ce dont l’agent a besoin pour trouver son exécutable, son répertoire temporaire, le endpoint MCP ou le shim stdio, ainsi que le chemin où le wrapper écrit son enregistrement. N’héritez pas par habitude de l’intégralité de l’environnement shell du développeur. Un environnement complet importe des identifiants sans rapport, une configuration cloud, des jetons de registre de paquets et d’anciens paramètres SSH qui peuvent faire échouer le test pour une mauvaise raison.

L’enregistrement attendu au lancement a cette forme :

{
  "argv": ["agent-command", "run", "tests/agent-task.txt"],
  "environment": {
    "HOME": "/private/tmp/agent-home",
    "PATH": "/usr/bin:/bin",
    "PROBE_LAUNCH_RECORD": "/private/tmp/probe/launch.json"
  }
}

Les chemins exacts n’ont pas d’importance. Le résultat essentiel est que l’enregistrement ne contient ni le canari HTTP ou SSH, ni sa forme base64, ni sa forme encodée pour une URL, ni le nom d’un fichier contenant une clé privée.

Ne masquez pas cet enregistrement avant le passage du scanner. Le masquage appartient aux rapports destinés aux personnes. L’enregistrement brut est la preuve. Si un test de livraison n’enregistre qu’une version nettoyée, il peut prouver que le masque fonctionne tout en cachant précisément la fuite que vous deviez détecter.

Un instantané de lancement a toutefois une limite : il indique ce que le processus possédait au démarrage. Il ne permet pas de savoir si un appel d’outil ultérieur place un secret dans l’environnement d’un processus enfant ou dans un fichier temporaire. C’est pourquoi les tests suivants forcent l’agent à créer des éléments après son démarrage.

Les processus enfants font partie de la frontière de l’agent

Les agents lancent souvent des formateurs, des gestionnaires de paquets, des outils de test, des commandes Git, des clients SSH et des scripts. Si l’agent peut lancer un enfant, cet enfant peut écrire son environnement et ses arguments sur le disque, les renvoyer dans une sortie ou les transmettre à une requête réseau. Considérez chaque enfant comme accessible depuis l’agent, sauf raison technique solide du contraire.

Donnez à l’agent une tâche inoffensive qui exécute un programme de sonde après l’achèvement d’une action de passerelle. La sonde affiche ses propres arguments et son environnement sous une forme lisible par une machine. Comme elle est enfant de l’environnement d’exécution de l’agent, elle observe les valeurs propagées par celui-ci à ce moment-là.

Utilisez une sonde qui écrit dans un répertoire contrôlé plutôt que de renvoyer un énorme dump d’environnement dans la conversation du modèle. Vous testez une fuite, vous ne devez pas l’inviter dans la transcription.

#!/usr/bin/env python3
import json
import os
import pathlib
import sys

path = pathlib.Path(os.environ["PROBE_CHILD_RECORD"])
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(
    json.dumps(
        {"argv": sys.argv, "environment": dict(os.environ)},
        sort_keys=True,
    ),
    encoding="utf-8",
)
print("child probe completed")

Demandez à l’agent d’effectuer d’abord une action authentifiée, puis d’appeler la sonde avec un argument banal comme after-http-action. Exécutez la même séquence après SSH. Si l’implémentation de la passerelle injecte un identifiant dans une variable d’environnement pour un assistant et laisse cet assistant devenir un descendant de l’agent, ce test le détectera.

Vérifiez séparément les chemins passés en arguments. Les développeurs savent que les variables d’environnement peuvent fuiter, mais les arguments de commande sont souvent pires, car l’inspection des processus, l’historique shell, le formatage des erreurs et les collecteurs de diagnostic peuvent les enregistrer. Apple indique qu’un processus peut accéder à ses propres arguments via CommandLine.arguments, et que ProcessInfo expose les arguments et l’environnement. Les secrets passés dans argv sont donc immédiatement observables par le code exécuté dans le processus.

N’acceptez pas un argument comme --token-file=/private/tmp/secret sous prétexte que les octets du jeton sont absents. Le test doit aussi inspecter ce chemin. Si l’agent peut lire le fichier, il possède le secret. Si le fichier est uniquement lisible par un processus de passerelle distinct et ne passe jamais par des répertoires contrôlés par l’agent, consignez ce fait dans la configuration du test.

Pour SSH, recherchez autre chose que le texte d’une clé privée. Échouez si vous trouvez le chemin d’un fichier d’identité, un socket d’agent qui expose l’identité de test, un fichier known_hosts généré contenant des éléments privés ou une ligne de commande comportant un mot de passe. Une clé privée stockée dans un fichier temporaire reste une clé privée, même si l’agent ne reçoit que le chemin.

Les actions réussies ont besoin de transcriptions hostiles

Vérifiez l’audit hors ligne
Exécutez sp audit verify sur le texte chiffré, sans avoir besoin d’une clé de coffre.

Un scénario favorable qui renvoie 200 OK ne prouve presque rien. Il prouve seulement que quelqu’un a effectué une requête. Faites demander à l’agent une véritable action par la passerelle, puis conservez toutes les transcriptions et traces d’outil produites du côté agent.

Le test HTTP doit appeler un endpoint qui confirme le compte de test authentifié sans reproduire les en-têtes de la requête. Une réponse utile est un objet fixe comme celui-ci :

{
  "account": "gateway-test-http",
  "accepted": true,
  "request_label": "run-2026-07-22-http-01"
}

L’agent peut raisonner à partir de cette réponse. Il n’a besoin ni du jeton bearer, ni du schéma d’autorisation, ni du nom d’en-tête injecté, ni d’un préfixe de jeton masqué. Si l’interface d’action renvoie un objet de requête à des fins de débogage, faites-en une cible de test distincte, car il s’agit d’une source probable de fuite. Un champ appelé request_headers est un défaut de conception, sauf s’il est garanti qu’il supprime les données d’identification avant leur franchissement de la frontière de l’agent.

Pour SSH, demandez à l’hôte d’accepter une commande fixe comme report-status <run-label>. Le serveur enregistre le compte authentifié, la commande demandée et l’étiquette. Il renvoie une réponse comme status recorded. L’agent ne doit recevoir ni la clé privée, ni une exportation de l’agent SSH, ni une transcription de l’échange d’authentification.

Enregistrez ces artefacts du côté agent :

  • L’invite initiale de l’agent et la transcription du modèle.
  • Les messages MCP bruts ou les requêtes et réponses d’action équivalentes.
  • La sortie standard et la sortie d’erreur des commandes contrôlées par l’agent.
  • Les journaux de débogage des outils, les journaux de nouvelles tentatives et les fichiers d’événements structurés.
  • Les fichiers écrits dans l’espace de travail de l’agent, le répertoire temporaire et le répertoire de cache configuré.

Collectez-les avant qu’une routine de nettoyage ne supprime les preuves. Analysez ensuite les octets exacts, et pas seulement le texte décodé en UTF-8. Un secret peut apparaître dans des échappements JSON, un encodage en pourcentage, du base64, une trace compressée ou un fichier dont des octets invalides font ignorer la correspondance lors d’une recherche textuelle ordinaire.

Le shim sp mcp de Sallyport convient bien à ce test, car il permet d’exercer le même chemin MCP habituel qu’utilise un agent tout en conservant les identifiants dans le coffre de l’application.

Ne confondez pas une transcription masquée avec une preuve. Une ligne de journal comme Authorization: [REDACTED] peut convenir à la sortie destinée à l’opérateur, mais l’objet d’événement brut qui l’a produite peut encore contenir le jeton. Capturez les données avant leur formatage d’affichage, puis testez séparément le formateur. Ce sont deux obligations différentes.

C’est lors des erreurs que l’isolation des secrets cède généralement

Une passerelle peut garder une réponse de réussite propre et divulguer malgré tout un secret lorsqu’une erreur survient. Les chemins d’erreur attirent le contexte de débogage, la reconstruction de requêtes, les chaînes d’exceptions et les messages de nouvelle tentative. Exécutez-les volontairement.

Commencez par des échecs HTTP qui surviennent à différents moments :

  1. Faites renvoyer au serveur de test un 401 non secret après réception d’un canari valide. L’agent doit apprendre que l’authentification a échoué, pas connaître la valeur de l’en-tête envoyé.
  2. Faites renvoyer au serveur une erreur 500 dont le corps contient l’étiquette publique d’exécution. La passerelle peut renvoyer un corps d’erreur limité, mais elle ne doit pas ajouter les en-têtes de la requête ni un équivalent de curl.
  3. Fermez la connexion après que la passerelle a préparé l’authentification. Cela détecte les exceptions de bas niveau qui incluent des objets de requête dans leur description.
  4. Renvoyez un JSON mal formé après une authentification réussie. Les analyseurs incluent souvent la réponse fautive ou le contexte environnant dans une exception.
  5. Utilisez un nom DNS qui ne se résout pas pour une route de test. Cela détecte les sorties liées aux nouvelles tentatives et au diagnostic du endpoint.

Exécutez ensuite des échecs SSH qui surviennent avant et après l’établissement de la connexion. Utilisez un hôte dont l’identité est incorrecte, une commande distante qui se termine avec un statut différent de zéro et une commande forcée qui renvoie une erreur contrôlée. Ne testez pas une mauvaise clé privée en renvoyant une clé de test à l’agent ou en demandant au serveur de l’enregistrer. L’agent doit uniquement recevoir une classification comme connection rejected ou remote command failed, accompagnée d’une étiquette de requête sûre.

Testez aussi l’état verrouillé. Tant que le coffre est verrouillé, une action doit échouer avant toute authentification réseau. La réponse peut indiquer que l’autorisation est indisponible. Elle ne doit contenir ni substitut de jeton, ni chemin vers le stockage des identifiants, ni nom de fichier d’identité SSH, ni nombre de caractères d’un secret. Après le déverrouillage, répétez l’action et exigez l’enregistrement de réussite côté serveur. Cette paire de tests détecte les implémentations qui construisent une demande d’identifiant avant de vérifier le verrouillage.

Un dispositif d’échec utile contient des assertions des deux côtés :

Agent side: the canary is absent from every collected artifact.
Gateway side: the attempted action has the expected safe error classification.
Server side: the expected request occurred, or did not occur for a locked vault test.

Cette dernière assertion évite les fausses certitudes. Si un test attend une erreur de nouvelle tentative, mais que la passerelle a rejeté l’appel plus tôt à cause d’une erreur de configuration, il peut réussir l’analyse de fuite sans avoir exercé le code dangereux.

Ne transmettez pas d’exceptions brutes à travers la frontière. Les objets d’erreur doivent contenir un identifiant d’action, une catégorie sûre, un message utile pour l’utilisateur et éventuellement une indication de nouvelle tentative. Ils ne doivent pas sérialiser la configuration de la requête à l’origine de l’exception. Le besoin de faciliter le débogage rend les dumps complets de requêtes populaires. Cela reste un mauvais choix par défaut lorsqu’un injecteur d’identifiants possède la requête.

Les artefacts de crash méritent un échec volontaire

Révoquez une exécution risquée de l’agent
Le journal Sessions de Sallyport vous permet de révoquer immédiatement une exécution de l’agent.

Un crash n’est pas un résultat d’API normal, ce qui explique pourquoi les équipes le négligent. C’est une erreur. Un développeur peut joindre un rapport de crash à un ticket, un script de support peut l’archiver et un système de diagnostic peut collecter les journaux associés. Si un secret y arrive, vous avez créé une fuite différée plutôt qu’une frontière sûre.

Après une action HTTP réussie, puis à nouveau après une action SSH réussie, faites avorter un processus de sonde côté agent. Gardez la cible du crash séparée de la passerelle. Vous cherchez à savoir si le côté agent a hérité ou enregistré des éléments secrets, pas si le fait de faire volontairement planter le détenteur des identifiants expose son état privé.

Sur macOS, recueillez le rapport via Console ou par le chemin de collecte des diagnostics de l’environnement de test, puis analysez le fichier non modifié. Apple décrit les rapports de crash comme des enregistrements détaillés de l’état d’une application et recommande d’analyser l’intégralité du rapport du système d’exploitation. Sa documentation indique également que ces rapports contiennent des informations sur le processus et l’environnement, comme l’identité du processus, son chemin, son processus parent, les horaires et l’état des threads.

L’absence du canari dans un rapport de crash macOS ne prouve pas qu’il n’a jamais été présent en mémoire. Un rapport standard n’est pas un dump mémoire complet. Cette limite ne justifie pas l’abandon du test. Elle signifie qu’il faut formuler correctement le résultat : le rapport n’a pas exposé le canari et le processus de l’agent ne l’a pas reçu par les autres canaux testés.

Analysez aussi les journaux de l’application et les archives de support créés autour du crash. Apple recommande aux développeurs de ne pas inclure d’informations sensibles dans les journaux. Considérez cela comme une exigence pour votre propre dispositif : si un afficheur d’exception enregistre un objet de requête, le test de crash doit échouer même si le rapport de crash du système d’exploitation est propre.

Si vous exécutez ensuite des tests similaires sous Linux, ajoutez les métadonnées des core dumps et la sortie du journal aux artefacts. Le manuel de systemd-coredump documente des champs pouvant stocker la ligne de commande et l’environnement d’un processus ayant crashé. Une suite qui analyse uniquement le fichier core et ignore ses métadonnées laisse de côté un chemin de fuite évident.

Analysez les octets, les encodages et les valeurs fragmentées

Approuvez le processus de l’agent une seule fois
Sallyport affiche l’autorité de signature du code d’un nouvel agent avant d’autoriser la session.

Un simple grep récursif vaut mieux que rien, mais il ne détecte pas les formes qui apparaissent dans les JSON, les URL, les traces et les paquets binaires. Construisez un scanner qui lit les fichiers comme des octets et recherche plusieurs transformations déterministes de chaque canari.

Générez au minimum ces éléments à rechercher pour chaque canari :

raw bytes
base64 text
URL encoded text
JSON escaped text
hex text
first half and second half separated by one newline

Le cas fragmenté détecte les wrappers de journalisation qui coupent les longues valeurs. Les autres cas détectent les systèmes qui sérialisent des données structurées avant de les écrire. Ne recherchez pas uniquement un préfixe de jeton. Un test de préfixe peut réussir si le jeton est tronqué après un nombre de caractères suffisant pour rester utilisable, et il peut échouer sur un identifiant sans rapport.

Un scanner compact peut signaler le chemin du fichier, le nom de la transformation et le décalage en octets sans afficher le secret lui-même :

import base64
import json
import pathlib
import urllib.parse

secret = bytes.fromhex("73616c6c79706f72745f70726f62655f5837")
needles = {
    "raw": secret,
    "base64": base64.b64encode(secret),
    "url": urllib.parse.quote_from_bytes(secret).encode(),
    "json": json.dumps(secret.decode()).encode(),
    "hex": secret.hex().encode(),
}

for path in pathlib.Path("artifacts").rglob("*"):
    if not path.is_file():
        continue
    data = path.read_bytes()
    for name, needle in needles.items():
        offset = data.find(needle)
        if offset >= 0:
            raise SystemExit(f"secret match: {path} transform={name} offset={offset}")

Le code signale volontairement un décalage plutôt que les octets correspondants. Un échec de test ne doit pas créer une seconde fuite dans la sortie CI. Ne conservez une copie destinée à l’analyse que si votre procédure d’incident l’exige, et gardez-la en dehors des journaux de compilation ordinaires.

Analysez les archives après les avoir décompressées dans un répertoire temporaire protégé. Analysez aussi les données compressées lorsque c’est possible, car une recherche brute ne voit pas un canari dans une charge utile compressée. Si votre client de télémétrie regroupe les événements, collectez le paquet avant qu’il ne quitte la machine de test. Découvrir plus tard que le scanner n’a inspecté que les fichiers locaux ne sert à rien si un jeton encodé est déjà parti chez un collecteur tiers.

Conservez une liste blanche pour les étiquettes publiques attendues, pas pour les secrets. Si un test échoue parce qu’un champ contient le nom du compte de test, demandez-vous si ce nom permet à lui seul d’accéder au système. N’ajoutez pas d’exclusions larges avant que le scanner ne repasse au vert. Chaque exclusion est une faille que vous oublierez de réexaminer.

Le rapport doit montrer à la fois l’absence du secret et l’action effectuée

Un bon rapport de test répond à quatre questions sans demander au lecteur de faire confiance à votre interprétation.

Premièrement, quelle action de la passerelle a réussi ou échoué ? Affichez l’étiquette publique d’exécution, le type d’action et l’observation côté serveur. Deuxièmement, quels artefacts avez-vous collectés ? Listez l’instantané de lancement, l’instantané des processus enfants, le paquet de transcriptions, l’arborescence de l’espace de travail, les sorties d’erreur et les artefacts de crash. Troisièmement, quelles transformations du secret le scanner a-t-il recherchées ? Quatrièmement, une lecture a-t-elle échoué à cause d’un problème de permissions, d’une hypothèse d’encodage incorrecte ou d’une archive ignorée ?

Un artefact ignoré n’est pas une réussite. Marquez le test comme incomplet et faites échouer la suite, sauf si vous avez une raison documentée de considérer cet artefact comme extérieur à la frontière de l’agent. Cette règle agace pendant la mise en place de la CI. Elle évite aussi la situation prévisible où un test annonce une réussite parce qu’il ne pouvait pas ouvrir le répertoire contenant la fuite.

Conservez l’enregistrement d’action de la passerelle séparément du paquet d’artefacts de l’agent. L’audit de la passerelle peut prouver qu’une action protégée par un identifiant a eu lieu. Le paquet de l’agent peut prouver ce que l’agent a reçu. Les combiner dans un export pratique crée un nouvel emplacement inutile où des informations sensibles peuvent circuler.

Pour les vérifications avant livraison, imposez une condition de réussite stricte :

PASS only when the server confirms the intended action,
all required artifacts were collected,
and no raw or transformed canary appears in agent reachable material.

Ajoutez ensuite des contrôles négatifs. Exécutez un dispositif volontairement défectueux qui transmet le canari dans une variable d’environnement ou une fausse réponse de débogage. Le scanner doit le faire échouer. Un test qui ne démontre jamais sa capacité à détecter une fuite connue relève du théâtre.

Exécutez la suite chaque fois qu’une personne modifie l’injection d’identifiants, le code de lancement des processus, le transport MCP, le formatage des erreurs, la journalisation, la collecte de support ou la gestion SSH. Ces changements peuvent sembler sans rapport lors d’une revue, mais ce sont précisément les endroits où les identifiants s’échappent. Gardez une version réduite dans les tests d’intégration ordinaires et réservez la collecte des crashs et des archives aux exécutions planifiées ou aux versions, si elle est coûteuse.

Le principe est simple : la passerelle peut utiliser un secret pour agir, mais l’agent ne doit pas acquérir ce secret comme donnée. Si votre test observe uniquement ce que l’agent dit, trop d’éléments restent hors de contrôle. Faites agir l’agent, faites-le échouer, faites-lui créer un processus enfant, faites-le crasher, puis analysez ce qui reste.

FAQ

Un test en boîte noire peut-il prouver qu’un agent IA ne voit jamais un secret ?

Un test en boîte noire ne peut pas prouver une affirmation absolue concernant chaque instruction du processeur ou chaque version future. Il peut en revanche établir une affirmation beaucoup plus utile : lors d’exécutions contrôlées avec des identifiants canaris uniques, le processus de l’agent, ses descendants, ses transcriptions et les artefacts collectés ne contiennent aucun élément d’identification. Faites-en une condition de mise en production, pas une preuve philosophique.

Dois-je utiliser de vraies clés API pour tester les fuites de secrets ?

Utilisez un identifiant qui ne fonctionne que sur un compte ou un hôte de test jetable, puis attribuez un marqueur différent à chaque exécution. N’utilisez jamais de jeton de production, même si vous prévoyez de le révoquer ensuite. Un secret de test doit rester sans danger s’il est exposé pendant un test en échec, puisque les échecs sont précisément ce que l’exercice cherche à détecter.

Quels artefacts dois-je analyser pour détecter des secrets API divulgués ?

Inspectez l’environnement de lancement de l’agent, ses arguments de commande, son répertoire de travail, ses processus enfants, les conversations enregistrées, les journaux des requêtes d’outils, la sortie standard, la sortie d’erreur, les rapports de crash et les paquets de support. Analysez les artefacts bruts avant toute modification par une couche de masquage. Un rapport masqué peut sembler propre alors que le fichier original contient encore le secret.

Est-il acceptable qu’un appel d’outil de l’agent contienne un secret si le modèle ne le mentionne pas ?

Non. Voir un jeton dans une transcription d’appel d’outil prouve qu’il a franchi la frontière de l’agent, même si le modèle ne l’a jamais répété. La propriété de sécurité concerne les données auxquelles le processus de l’agent peut accéder, pas le fait qu’il choisisse ou non de les reproduire.

Comment tester des identifiants SSH sans exposer une clé privée ?

Les tests SSH nécessitent un hôte jetable qui enregistre le compte authentifié, la commande demandée et le résultat de la session, sans afficher de clé privée ni d’identifiant. Utilisez un compte de test limité et, si possible, une commande forcée. Vous devez démontrer que l’action a bien eu lieu et qu’aucun octet de clé privée n’est apparu dans les artefacts de l’agent.

Les rapports de crash contiennent-ils des clés API ou des secrets SSH ?

Les rapports de crash méritent d’être vérifiés, car ils sont souvent copiés dans des rapports de bugs et des paquets de diagnostic. Leur absence ne prouve pas qu’un secret n’a jamais atteint la mémoire, puisqu’un rapport de crash normal n’est pas une image mémoire complète. Ils permettent néanmoins de détecter des fuites évitables dans les messages d’exception, les métadonnées de processus, les lignes de commande et les journaux de l’application.

Dois-je tester les actions lorsque le coffre d’identifiants est verrouillé ?

Testez les deux situations. Une passerelle doit refuser une action lorsque son coffre est verrouillé, et ce refus ne doit pas transmettre à l’agent un substitut d’identifiant, la longueur d’un identifiant ou une erreur trompeuse liée aux identifiants. Après le déverrouillage, répétez la même action et vérifiez que seul le résultat prévu devient visible.

Pourquoi tester à la fois l’environnement de l’agent et ses processus enfants ?

Un wrapper de processus capture l’environnement et les arguments fournis au lancement. Une commande de sonde lancée par l’agent capture l’environnement hérité par les processus enfants plus tard dans l’exécution. Les deux vérifications sont nécessaires, car un démarrage propre n’exclut pas une injection ultérieure.

Qu’est-ce qui doit faire échouer un test de fuite de secret ?

Faites échouer la compilation si le canari exact apparaît dans un artefact lisible par un agent, un développeur, un intervenant du support ou un système de télémétrie. Échouez aussi si une forme transformée apparaît, par exemple encodée dans une URL, en base64, échappée pour JSON ou répartie sur plusieurs lignes. Un test de sécurité qui ignore les transformations vérifie surtout le scénario favorable.

À quelle fréquence dois-je exécuter les tests de fuite de secrets d’un agent ?

Exécutez la suite complète à chaque modification touchant l’injection d’identifiants, le transport des outils, le lancement des processus, la journalisation, la gestion des erreurs ou les rapports de crash. Lancez un contrôle canari plus léger dans les tests d’intégration habituels. Les frontières de sécurité se brisent dans le code qui relie ces composants, attendre une version majeure est donc trop tardif.

Sallyport

Sallyport exécute les appels d'API et les commandes SSH à la place de votre agent IA. Les clés restent dans un coffre-fort local sur votre Mac ; vous approuvez chaque exécution et chaque action est consignée dans un journal scellé.

© 2026 Sallyport · Open source sous Apache-2.0 · Oleg Sotnikov