# 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 :

```text
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 :

```python
#!/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 :

```json
{
  "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.

```python
#!/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

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 :

```json
{
  "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 :

```text
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

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

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 :

```text
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 :

```python
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 :

```text
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.
