PyRIT donne à chaque convertisseur et à chaque scorer un identifiant, une empreinte des réglages qui changent ce que fait le composant. Les noms du registre en découlent et les résultats en cache s'appuient dessus. Quand deux composants qui se comportent différemment partagent la même empreinte, rien ne plante. Leurs résultats sont simplement attribués à la mauvaise configuration.

Le même bug, quinze fois

En ouvrant l'issue, j'ai compté : 15 pull requests fusionnées de 8 auteurs, depuis le 6 octobre, avaient chacune corrigé ce même défaut pour un seul composant. Deux étaient à moi. La classe de base énonçait déjà la règle dans sa docstring : "Only include params that affect the component's behavior/output." Rien ne la vérifiait, donc chaque trou attendait que quelqu'un le remarque.

Un test, tout le registre

Le test parcourt ConverterRegistry, change un réglage du constructeur à la fois et exige que l'empreinte change. Un nouveau convertisseur ou un nouveau paramètre est couvert le jour de son arrivée. Un réglage doit avoir une valeur d'exemple ou une raison écrite de rester hors de l'identité, donc rien n'est ignoré en silence.

assert len(set(hashes)) == len(hashes), (
    f"{converter_name}.{parameter_name} changes the converter but not its identifier{picked}"
)

Il a trouvé trois trous : PolicyPuppetryConverter qui identifie un modèle personnalisé par son seul nom, TextJailbreakConverter qui donne le même identifiant à tous les modèles en ligne, et CodeChameleonConverter dont les fonctions personnalisées n'atteignaient jamais l'identifiant. Chacun est un xfail strict lié à sa propre issue : le corriger fait passer le test au rouge tant que la marque reste en place.

Puis la revue a cassé mon test, deux fois

Un test contre les fausses identités avait ses propres faux succès. Un mainteneur a changé une ligne de code de production pour que les décalages 1 et 2 de CaesarConverter enregistrent la même valeur. Le test est resté vert, parce qu'il jetait l'empreinte de référence après le contrôle de répétabilité.

Puis le même mainteneur a ajouté un compteur actif seulement avec un réglage non par défaut. Chaque réglage modifié n'était construit qu'une fois, donc ce bruit passait pour un réglage bien identifié. Désormais chaque configuration est construite deux fois, et la référence entre dans le contrôle de collision dès que sa valeur est connue. Les deux mutations échouent.

Tester le test

Le contrôle final a fait la même chose à grande échelle : 83 mutations de l'identifiant seul. 70 échouent et nomment le réglage. 3 corrections de contenu échouent en XPASS. 6 restent vertes comme il se doit, 2 ne changent rien d'observable, et 2 touchent des limites que la pull request nomme clairement, avec ce qu'elle n'attrape pas.

Ce que j'en retiens

Quinze correctifs pour une seule règle, c'est une règle sans test. J'avais corrigé ce bug deux fois avant de compter. Le changement fusionné, c'est 1 000 lignes de tests et zéro ligne de code de production. Dans les mots du mainteneur : "This should help us stop discovering the same identifier problem one component at a time."

Ayoub Hammoudi, Ingénieur logiciel, full-stack et plateformes IA. github.com/Ayoubhm07