Aller au contenu
Myrhex
English

Dossier de mission05 / 06

Bulwark

Bibliothèque de sécurité2026Open source

Une défense contre l’injection de prompt pour tout ce qui donne du texte non fiable à un modèle ou le laisse appeler des outils.

Sept couches, trois langages

Bulwark se place entre un texte non fiable et un modèle de langage, en sept couches : décoder, assainir, détecter, baliser, durcir, valider et ancrer. Elle existe en Python, TypeScript et Swift, sans aucune dépendance obligatoire à l’exécution, et ses 131 signatures d’injection couvrent 15 langues.

Les signatures sont écrites une seule fois, en Python, puis générées dans les deux autres. La première fois que j’ai comparé les trois implémentations, elles divergeaient sur 529 documents. Générer à partir d’une source unique a fait tomber ce chiffre à 67 d’un coup, et la traque des écarts restants a mis au jour de vrais bugs.

Mesuré, pas proclamé

Depuis la 1.0, elle s’évalue sur 888 documents étiquetés, 539 attaques et 349 textes inoffensifs, écrits indépendamment du code. Chaque étiquette note ce que dirait un ingénieur sécurité rigoureux, jamais ce que Bulwark répond aujourd’hui : chaque raté est donc un rapport de bug reproductible, pas une tautologie.

Sur ce jeu volontairement hostile, elle attrape 39 % des attaques avec une précision de 87 %, et signale à tort 9 % des documents inoffensifs, 3 % une fois mis de côté ceux qui sont délibérément limites. Ce sont des chiffres honnêtes pour un corpus construit pour la battre. Le garde-fou refuse toute modification qui attrape moins, signale davantage de textes inoffensifs, ou laisse passer une seule attaque qu’elle attrapait auparavant.

Un seul saut de ligne

Un jour, un simple saut de ligne a battu les signatures phares. Elles relient des mots déclencheurs sur de courtes distances, et le point d’une expression régulière ne franchit pas un retour à la ligne : couper « ignore all previous instructions » sur deux lignes faisait passer son score de 0,86 à zéro. La détection travaille désormais sur quatre vues du texte : tel qu’il est écrit, avec les lettres sosies et le leetspeak ramenés à la normale, avec les espaces aplatis et les mots coupés recollés, et sans les accents.

Les bugs que seul un second langage révèle

En Swift, le ı turc sans point échappait à la comparaison insensible à la casse, si bien qu’une balise écrite <ımg> devenait invisible pour le contrôle de sortie : une fuite de données en un seul caractère. En TypeScript, le texte était mesuré en unités UTF-16, de sorte qu’un document fait entièrement de caractères hostiles situés hors du plan de base d’Unicode ressortait contaminé à 4,7 % au lieu de 100 %. Relire le code n’avait trouvé ni l’un ni l’autre. Comparer les langages a trouvé les deux.

Protéger la boucle de l’agent

ToolGuard voit un agent comme trois points de passage obligés : ce qui entre dans un outil, ce qui en ressort, et la boucle elle-même. Il lit les noms d’hôte comme le fait la bibliothèque C, parce que 0177.0.0.1, 0x7f.0.0.1 et 2130706433 désignent tous votre propre machine, et dès qu’une session a lu du contenu injecté, il met en attente tout ce qui va au-delà de la lecture seule, jusqu’à ce qu’une personne confirme.

Rampart masque les données personnelles avant qu’elles n’atteignent un modèle. Ses marqueurs portent un nonce aléatoire : un attaquant ne peut donc pas glisser [EMAIL_1] dans une page et obtenir qu’une vraie adresse y soit réinsérée, là où il peut la lire.

Ce qui n’est pas fait

La 1.1.0 est taguée sur GitHub, mais npm et PyPI servent encore la 0.3.0. La version Swift de Rampart couvre six types de données personnelles, contre douze pour les deux autres. Et l’injection de prompt n’est pas un problème résolu : Bulwark rend les attaques plus difficiles et les échecs visibles. Elle ne les rend pas impossibles.