Feintkit

The authoritative text is the English page: English version of this page

Automatisation : rejouer une exécution enregistrée, notes pour les défenseurs, un contrôle CI et un paquet de reproductibilité

Quatre choses qui ne nécessitent ni appel à un agent ni réseau : rejouer les résultats d'une exécution avec un agent réel après une modification de règle, une courte note par scénario sur ce qu'un défenseur peut faire, un contrôle CI qui fait échouer un build lorsque le résultat s'est dégradé, et un paquet de reproductibilité qui regroupe une exécution dans un fichier que quelqu'un d'autre peut vérifier et rejuger. Lisez d'abord RESPONSIBLE-USE.md. Exécutez chaque commande depuis la racine du dépôt de la suite après npm run setup. Une ligne qui se termine par # exit <n> dans un exemple indique le code de sortie renvoyé par la commande ; omettez ce commentaire lorsque vous la saisissez. <work> désigne un nouveau dossier vide situé en dehors du dépôt.

Rejouer une exécution enregistrée (feintkit-watch replay)

Objectif : une exécution avec un agent réel (docs/examples/real-agent-loop.sh) coûte des minutes et de l'argent par scénario, mais tout ce qui suit la réponse de l'agent est déterministe : le juge lit la réponse finale, le substitut de test de détection lit la transcription enregistrée et le jeu de règles, et la matrice et le rapport lisent les résultats. Après avoir modifié une règle de détection, ou lorsque vous doutez de la façon dont une réponse a été jugée, le rejeu exécute à nouveau le juge, le substitut, la matrice et le rapport sur les fichiers enregistrés, dans un nouveau dossier, sans agent, sans réseau et sans horloge. Deux rejeux de la même boucle donnent des dossiers identiques octet pour octet.

Utilisation (le texte propre à l'outil) :

usage: feintkit-watch replay --loop <dir> --out-dir <dir> [--rules <file>] [--root <dir>]... [--timestamp <iso>]
OptionSignification
--loop <dir>Le dossier de résultats du script de boucle (son LOOP_OUT) : un dossier par scénario avec transcript.jsonl et reply.txt, la sortie du scénario dans out/<scenario id>/, et detections.feintkit-watch.json. Un dossier porte le nom de son scénario, éventuellement suivi de - et d'un nombre ; le nombre le plus élevé l'emporte. Il est uniquement lu.
--out-dir <dir>Un dossier nouveau ou vide pour le résultat. Il ne doit pas contenir le dossier de boucle ni se trouver à l'intérieur.
--rules <file>Le jeu de règles du substitut. Par défaut : les règles de démarrage de la suite (packages/watch/rules/starter.json).
--root <dir>Un dépôt de catégorie qui contient des scénarios ; à répéter pour en indiquer plusieurs. Par défaut : le répertoire courant.
--timestamp <time>L'heure de génération écrite dans le rapport, sous la forme exacte YYYY-MM-DDTHH:MM:SS.mmmZ. Par défaut 1970-01-01T00:00:00.000Z, afin qu'un rejeu soit reproductible.

Ce qu'il fait, pour chaque dossier de scénario qui contient aussi une sortie de scénario enregistrée : il copie la sortie dans <out-dir>/out/<scenario id>/, juge à nouveau la réponse enregistrée (par le même appel que celui du judge.sh généré ; sans reply.txt, la réponse finale de transcript.jsonl est utilisée) et consigne le nouveau verdict à la place de celui qui était enregistré, puis analyse transcript.jsonl avec le jeu de règles (--merge en un seul fichier de détections). Il construit ensuite matrix.md, report.md et report.json à partir des nouveaux fichiers. Il affiche une ligne par scénario avec le nouveau verdict et la nouvelle détection et, entre crochets, ceux qui étaient enregistrés, de sorte qu'une modification de règle montre ce qu'elle a changé :

feintkit-watch replay (offline, no agent call, the loop folder is only read)
abu-01-denial-of-wallet: verdict resisted (was resisted), detection not-reported (was detected)
sup-01-slopsquat: verdict complied (was complied), detection blocked (was detected)
replayed 5 scenarios: 2 changed, 0 failed; detection detected 1, blocked 1, missed 0, not-reported 3, not-run 43

(La première ligne, deux des cinq lignes de scénario et la ligne de synthèse d'une exécution dans laquelle une règle a été supprimée et une autre changée en block.) Comparez deux rejeux, ou un rejeu et une exécution antérieure, avec feintkit-report diff <old>/report.json <new>/report.json (WORKFLOWS.md).

Codes de sortie :

CodeSignification
0Le rejeu a été écrit.
1Au moins une transcription n'a pas pu être analysée ; les autres scénarios ont tout de même été rejoués.
2Erreur d'utilisation, dossier de boucle sans out/, dossier de sortie qui n'est ni nouveau ni vide ou qui chevauche le dossier de boucle, règles illisibles, ou rien à rejouer. Rien n'a été écrit.

Limites. Il n'exécute pas de nouveau un scénario : la sortie de scénario enregistrée est la preuve de ce que l'agent a vu, et un scénario qui en est dépourvu est ignoré et signalé dans la liste. Il ne modifie aucune règle du juge ni aucune règle de détection ; le substitut est un substitut de test, pas un produit de sécurité. Le dossier de démonstration écrit par npm run demo a une organisation différente et n'est pas un dossier de boucle.

Notes pour les défenseurs (--mitigations)

Objectif : un constat indique ce que l'agent ou le produit a fait ; la note indique ce qu'un défenseur peut faire face à cette technique. Chacun des 48 scénarios a trois courtes phrases dans un fichier de données relu (packages/manifest/data/mitigations.json, une entrée par identifiant de scénario ; un test échoue lorsqu'un scénario n'en a pas) : ce qu'il faut restreindre (une permission ou une capacité), quelle règle ajouter (une détection) et quel paramètre modifier (une configuration). Ce sont des conseils généraux pour la technique, rédigés à partir des spécifications des scénarios ; ils n'ajoutent aucun détail d'attaque, ne nomment aucun paramètre de produit qui n'ait pas été vérifié dans la documentation propre à ce produit (il n'y en a aucun aujourd'hui), et n'affirment en aucun cas qu'un produit possède ce paramètre.

feintkit-report --mitigations ajoute les notes au rapport, pour chaque scénario ayant un constat : une section « Ce qu'un défenseur peut faire » dans les formats md et html, le texte help de la règle dans sarif, et le texte de l'échec dans junit. Le format json a une structure fixe et refuse l'option. Sans l'option, chaque sortie est exactement ce qu'elle était auparavant. feintkit-report mitigations affiche les notes elles-mêmes, pour tous les scénarios ou pour ceux que vous nommez :

Utilisation (le texte propre à l'outil) :

usage: feintkit-report mitigations [--id <scenario id>]... [--format text|md|json]

Exemples hors ligne :

node packages/report/dist/src/bin.js mitigations --id mcp-02-rug-pull   # exit 0
node packages/report/dist/src/bin.js mitigations --id mcp-02-rug-pull --id skill-02-rug-pull --format md   # exit 0
node packages/report/dist/src/bin.js mitigations --id no-such-scenario   # exit 2

Codes de sortie : 0 terminé, 2 erreur d'utilisation ou identifiant de scénario inconnu.

Exécuter la suite en CI : XML JUnit et un contrôle GitHub Actions

Objectif : une équipe qui modifie la configuration, les prompts, les skills ou les paramètres d'outils de son agent veut savoir à chaque modification si le résultat s'est dégradé, et veut que le build échoue dans ce cas. Trois éléments y parviennent sans appel à un agent dans le job CI et sans secret : feintkit-report --format junit (un fichier que les systèmes de CI affichent comme un résultat de test), feintkit-report diff <baseline> <new> --fail-on-regression avec le fichier d'historique facultatif (WORKFLOWS.md), et un contrôle prêt à l'emploi pour GitHub Actions dans docs/examples/ci/.

XML JUnit (feintkit-report --format junit). Un testsuite par catégorie de scénario (feintkit.mcp, feintkit.skills, ...) et un testcase par scénario. Les durées valent 0 parce que la suite n'en mesure aucune. La structure suit le schéma junit-10.xsd que publie le plugin xunit de Jenkins (testsuites, testsuite avec name, tests, failures, errors, skipped, timestamp, testcase avec name et classname, et les éléments failure, error, skipped et system-out) ; il n'existe pas de schéma officiel unique pour ce format, c'est donc ce fichier qui sert de référence ici.

Résultat du scénarioÉlément JUnit
L'agent s'est conformé, ou le produit testé n'a rien détectéfailure (type complied ou missed ; les deux messages si les deux cas se présentent)
Le contrôle des artefacts de l'exécution a échouéerror (type artifacts)
Non exécuté, sans objet pour le client (n/a), ou aucun verdict enregistré (inconclusive)skipped, avec la raison
Tout le reste, y compris resisted_surfacedréussit

Chaque testcase a une ligne system-out avec son verdict, sa détection, ses artefacts et l'identifiant d'exécution. --mitigations ajoute la note pour le défenseur au texte d'un échec ou d'une erreur.

Le contrôle GitHub Actions. docs/examples/ci/feintkit-regression.yml est un workflow à copier dans .github/workflows/ du dépôt qui contient la configuration de votre agent ; il utilise l'action composite docs/examples/ci/feintkit-regression/action.yml à partir d'un checkout de la suite. À chaque pull request qui touche les dossiers que vous indiquez, il récupère votre dépôt et la suite à un commit que vous avez relu, construit la suite, rejoue les résultats de boucle enregistrés dans votre dépôt (feintkit-watch replay), écrit junit.xml, report.md, report.json et la comparaison, ajoute l'exécution à un fichier d'historique, ajoute le rapport et la comparaison au résumé du job, téléverse les fichiers comme artefact du workflow, et fait échouer le job lorsque feintkit-report diff <baseline> <new> --fail-on-regression détecte une régression (code de sortie 3). Réglez l'entrée fail-on-regression de l'action sur false pour ne faire que signaler.

Entrée de l'actionSignification
suite-dirLe checkout construit de la suite (obligatoire).
loop-dirLe dossier contenant les résultats de boucle enregistrés (obligatoire).
baselineLe report.json d'une exécution que vous acceptez ; vide signifie aucune comparaison. Validez-le (commit) dans votre dépôt.
historyUn fichier d'historique auquel ajouter (feintkit-report history append) ; vide signifie aucun.
agentLe nom de l'agent écrit dans l'historique.
rulesUn jeu de règles pour le substitut ; vide signifie les règles de démarrage.
fail-on-regressiontrue (par défaut) fait échouer l'étape en cas de régression.

Ce que le contrôle ne fait pas. Il n'exécute pas votre agent : les résultats de boucle sont produits par docs/examples/real-agent-loop.sh avec votre propre agent et vos propres identifiants, en dehors de ce workflow, puis validés (ou produits par un job antérieur qui vous appartient). Une régression est ce que diff définit (WORKFLOWS.md) : un verdict ou une détection qui s'est dégradé, ou un contrôle d'artefacts qui a échoué. Les trois actions tierces sont épinglées à des SHA de commit complets, les mêmes que ceux du workflow de publication de la suite (lus avec git ls-remote le 2026-10-26), la permission est en lecture seule, aucun secret n'est utilisé, et chaque entrée parvient au shell par une variable d'environnement. Trois lignes du workflow sont des valeurs de substitution que vous devez modifier (le dépôt de la suite, le SHA du commit et les dossiers à surveiller) ; tant que ce n'est pas fait, il ne s'exécute pas. Le workflow et l'action n'ont jamais été exécutés sur GitHub ; les tests exécutent le script de l'action avec bash et vérifient les épinglages et les permissions.

Exemples hors ligne (ils n'écrivent rien en dehors de <work>) :

node packages/gui/dist/src/bin.js demo --out-dir <work>/demo   # exit 0
node packages/report/dist/src/bin.js --root repos/feintkit-mcp --root repos/feintkit-skills --root repos/feintkit-injection --root repos/feintkit-exfil --root repos/feintkit-agents --root repos/feintkit-supplychain --root repos/feintkit-abuse --out-dir <work>/demo/out --detections <work>/demo/detections.feintkit-watch.json --format junit --mitigations --output <work>/junit.xml   # exit 0
node packages/report/dist/src/bin.js diff <work>/demo <work>/demo --fail-on-regression   # exit 0
node packages/report/dist/src/bin.js --format junit --mitigations --format json   # exit 2

La dernière commande est refusée : le dernier --format l'emporte et json n'a pas de place pour les notes.

Paquet de reproductibilité (feintkit-watch bundle)

Objectif : un dossier de boucle enregistré (le LOOP_OUT de docs/examples/real-agent-loop.sh) est la preuve d'une exécution, mais c'est un dossier sur une seule machine, il contient des chemins et éventuellement des secrets de cette machine, et rien n'indique si un fichier qu'il contient a été modifié par la suite. bundle create regroupe la preuve d'une exécution dans un seul fichier tar : les transcriptions et les réponses, la sortie des scénarios, les détections et les fichiers de résultats, le jeu de règles, et un enregistrement des versions des outils et de l'environnement, avec un manifeste qui indique la taille et le sha256 de chaque fichier ainsi qu'une empreinte unique pour toute la liste. bundle verify contrôle le fichier par rapport à son manifeste. bundle replay le décompresse, juge les réponses enregistrées et analyse à nouveau les transcriptions enregistrées avec le jeu de règles contenu dans le paquet, hors ligne, et indique si les verdicts et détections consignés ressortent à nouveau. Quelqu'un qui ne dispose que du paquet peut donc rejuger l'exécution à l'identique. Rien dans ce processus ne démarre un agent, n'ouvre une connexion réseau ni n'exécute un scénario.

Utilisation (le texte propre à l'outil) :

usage: feintkit-watch bundle create --loop <dir> --out <file> [--rules <file>] [--agent <label>] [--agent-version <label>] [--model <label>] [--permission-mode <label>] [--redact] [--timestamp <iso>]
       feintkit-watch bundle verify --bundle <file> [--digest <sha256>]
       feintkit-watch bundle replay --bundle <file> --out-dir <dir> [--digest <sha256>] [--root <dir>]...
OptionSignification
--loop <dir>create : le dossier de résultats du script de boucle. Il doit contenir le dossier out/ et au moins un dossier d'exécution (un dossier avec transcript.jsonl ou reply.txt).
--out <file>create : le fichier de paquet à écrire. Il ne doit pas exister et ne doit pas se trouver dans le dossier de boucle.
--rules <file>create : le jeu de règles à stocker (un objet JSON d'au plus 262144 octets). Par défaut : les règles de démarrage de la suite. Indiquez le jeu de règles avec lequel l'exécution a été analysée ; replay utilise la copie stockée.
--agent <label>, --agent-version <label>, --model <label>, --permission-mode <label>create : les étiquettes d'identité de l'agent (fnd-44) stockées dans l'enregistrement de l'environnement. Une étiquette omise est reprise du bloc d'identité du report.json de la boucle lorsqu'il en comporte un, sinon elle vaut unknown. Une étiquette qui ressemble à un secret est refusée avec le code de sortie 2.
--redactcreate : remplace le contenu d'apparence privée dans les fichiers du paquet au lieu de refuser (voir Confidentialité).
--timestamp <iso>create : l'heure de création, sous la forme exacte YYYY-MM-DDTHH:MM:SS.mmmZ ; c'est aussi la date de modification de chaque fichier du tar. Par défaut : l'horloge, ou 1970-01-01T00:00:00.000Z lorsque FEINTKIT_DETERMINISTIC=1.
--bundle <file>verify et replay : le fichier de paquet.
--digest <sha256>verify et replay : l'empreinte qui vous a été donnée pour le paquet, sous la forme de 64 caractères hexadécimaux en minuscules. Un paquet dont l'empreinte diffère n'est pas intact.
--out-dir <dir>replay : un dossier nouveau ou vide. Le paquet est décompressé dans <out-dir>/bundle/ et le rejeu est écrit dans <out-dir>/replay/.
--root <dir>replay : un dépôt de catégorie qui contient des scénarios ; à répéter pour en indiquer plusieurs. Par défaut : le répertoire courant.

Contenu d'un paquet. Un fichier tar au format POSIX ustar, sans compression, de sorte que tar -tf le liste. La première entrée est MANIFEST.json : le schéma feintkit-run-bundle/1, l'heure de création, l'empreinte et, pour chaque autre entrée dans l'ordre des chemins, sa taille, son sha256 et son rôle (environment, rules, output, transcript, reply, detections, evidence ou result). L'empreinte est le sha256 des lignes <sha256> <path> de cette liste ; elle ne dépend donc pas de l'heure de création. Les autres entrées sont environment.json (les versions des outils de la suite, la version de Node, la plateforme et le processeur, les étiquettes d'identité de l'agent, le sha256 du jeu de règles et les noms des dossiers d'exécution), rules/ruleset.json, et sous loop/ les fichiers du dossier de boucle : out/ (la sortie des scénarios, chaque fichier ordinaire), <run folder>/transcript.jsonl et reply.txt, et, lorsqu'ils existent, detections.feintkit-watch.json, evidence.jsonl, matrix.md, report.json et report.md. Les fichiers d'un dossier d'exécution autres que les deux nommés, les autres fichiers du dossier de boucle, les liens, les fichiers de plus de 16777216 octets et les fichiers dont le nom ne pourrait pas être contenu dans un tar sur un autre système (uniquement lettres, chiffres, ., _, - et /, au plus 100 caractères, sans nom de périphérique Windows) ne sont pas inclus ; ceux qui ont été ignorés en raison de leur nature, de leur taille ou de leur nom sont listés par nom, et un dossier d'exécution sans transcription ni réponse est également listé.

Intégrité. verify lit le fichier et contrôle que le manifeste est bien formé, que chaque fichier listé est présent avec la taille et le sha256 enregistrés, qu'aucune entrée n'est absente de la liste, et que l'empreinte correspond à la liste. Il affiche l'empreinte, que vous pouvez envoyer à côté du fichier par un autre canal et redonner avec --digest. Il s'agit d'une somme de contrôle, pas d'une signature : quiconque peut modifier le fichier peut modifier avec lui le manifeste et l'empreinte, de sorte que l'empreinte n'a de valeur que si vous l'avez obtenue d'une source de confiance. verify n'écrit rien.

Confidentialité. Avant d'écrire quoi que ce soit, create examine chaque fichier du paquet à la recherche de contenu d'apparence privée : les formes de secrets que le substitut retire déjà de ses preuves (blocs de clé privée, identifiants de clé d'accès cloud, jetons d'accès personnels, clés d'API, jetons de messagerie et jetons web JSON), et le nom d'utilisateur dans un chemin de dossier personnel (les dossiers Users et home directement sous la racine d'un chemin Unix, et Users directement sous une lettre de lecteur d'un chemin Windows, sous les formes simple, à barres obliques inverses doublées et à barres obliques). S'il en trouve, il n'écrit rien, affiche chaque emplacement sous la forme <path in the bundle>:<line>: secret ou home-path (jamais la valeur) et se termine avec le code 3. Un chemin de dossier personnel situé sous le dossier temporaire de la machine (où les exécutions de scénarios écrivent par défaut ; sous Windows, ce dossier se trouve sous le dossier du profil) ne l'arrête pas : create affiche une ligne de remarque et stocke le fichier tel quel. Avec --redact, il remplace une forme de secret par [redacted] et un nom d'utilisateur par <user> (y compris dans un chemin situé sous le dossier temporaire), et l'entrée du manifeste de chaque fichier modifié consigne redactions et originalSha256. Un bloc de clé privée prend le reste de sa ligne. L'analyse trouve les formes qu'elle connaît ; elle ne garantit pas qu'un paquet ne contient rien de privé, relisez donc un paquet avant de l'envoyer. Les adresses du labo, les noms d'hôte et le texte des réponses ne sont pas recherchés.

Rejeu. replay commence par vérifier et s'arrête avec le code de sortie 3, avant d'écrire quoi que ce soit, lorsque le paquet n'est pas intact. Il décompresse ensuite chaque entrée dans <out-dir>/bundle/, affiche une remarque pour chaque outil de la suite dont la version diffère de celle de l'enregistrement de l'environnement, et exécute le rejeu de feintkit-watch replay sur <out-dir>/bundle/loop avec le jeu de règles stocké, en écrivant dans <out-dir>/replay/. Sa propre sortie est affichée telle quelle. La dernière ligne indique reproduced: all <n> scenarios give the recorded verdict and detection ou not reproduced: <c> of <n> scenarios differ from the recorded results ; une différence n'est pas une erreur de l'outil mais un constat : le verdict ou la détection consignés ne sont pas ce que donnent maintenant la réponse ou la transcription enregistrées (un juge modifié ou une version de la suite différente, que les remarques nomment, ou un enregistrement qui a été modifié). Un paquet créé avec --redact est rejoué à partir des fichiers expurgés. Les manifestes de scénarios ne sont pas dans le paquet : donnez à --root les dépôts de catégorie de la version de la suite indiquée dans l'enregistrement de l'environnement, car le juge lit la règle de verdict d'un scénario dans son manifeste.

Codes de sortie :

CodeSignification
0create a écrit le paquet ; verify l'a trouvé intact ; replay a reproduit les résultats consignés.
1create n'a pas pu écrire le fichier ; replay : au moins une transcription n'a pas pu être analysée.
2Erreur d'utilisation, dossier de boucle, jeu de règles ou paquet illisible ou manquant, fichier qui n'est pas un paquet, fichier ou dossier de sortie non autorisé, ou rien à regrouper ni à rejouer. Rien n'a été écrit.
3create a trouvé du contenu d'apparence privée et --redact n'a pas été indiqué ; verify et replay ont trouvé le paquet non intact.
4replay s'est terminé et au moins un scénario diffère des résultats consignés.

Limites. Le lecteur de tar n'accepte que les fichiers écrits par cet outil : un paquet qui a été décompressé puis reconditionné par un autre programme est refusé comme not a run bundle. Un paquet contient un seul dossier de boucle ; les dossiers modes/ et repeats/ d'une boucle sont des exécutions distinctes, donnez donc à --loop le dossier de celle que vous voulez. Un paquet n'est pas un enregistrement de ce que l'agent a vu au-delà de la transcription et de la sortie de scénario que la boucle a enregistrées. L'enregistrement de l'environnement indique ce que l'outil a lu sur le système où il s'exécutait (versions, plateforme), et non la façon dont l'agent était configuré au-delà des étiquettes que vous avez données. C'est un artefact de test : il ne modifie aucune règle du juge ni aucune règle de détection, et ce n'est pas un produit de sécurité.