Test de mutation pour Solidity avec Slither (slither-mutate)
Reading time: 6 minutes
tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :
HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le đŹ groupe Discord ou le groupe telegram ou suivez-nous sur Twitter đŠ @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépÎts github.
Le test de mutation "teste vos tests" en introduisant systématiquement de petites modifications (mutants) dans votre code Solidity et en relançant votre suite de tests. Si un test échoue, le mutant est tué. Si les tests passent toujours, le mutant survit, révélant un point aveugle dans votre suite de tests que la couverture de lignes/branches ne peut pas détecter.
Idée clé : la couverture montre que le code a été exécuté ; le test de mutation montre si le comportement est réellement vérifié.
Pourquoi la couverture peut ĂȘtre trompeuse
Considérez cette simple vérification de seuil :
function verifyMinimumDeposit(uint256 deposit) public returns (bool) {
if (deposit >= 1 ether) {
return true;
} else {
return false;
}
}
Les tests unitaires qui ne vérifient qu'une valeur en dessous et une valeur au-dessus du seuil peuvent atteindre 100% de couverture ligne/branche tout en omettant d'assertion la frontiÚre d'égalité (==). Un refactor vers deposit >= 2 ether réussirait toujours ces tests, brisant silencieusement la logique du protocole.
La mutation testing expose cette faille en mutant la condition et en vérifiant que vos tests échouent.
Opérateurs de mutation Solidity courants
Le moteur de mutation de Slither applique de nombreuses petites modifications changeant la sémantique, telles que :
- Remplacement d'opérateur :
+â-,*â/, etc. - Remplacement d'affectation :
+=â=,-=â= - Remplacement de constante : non-zĂ©ro â
0,trueâfalse - NĂ©gation/remplacement de condition Ă l'intĂ©rieur des
if/boucles - Commenter des lignes entiĂšres (CR: Comment Replacement)
- Remplacer une ligne par
revert() - Ăchanges de type de donnĂ©es : p.ex.,
int128âint64
Objectif : tuer 100% des mutants générés, ou justifier les survivants avec un raisonnement clair.
Exécuter la mutation testing avec slither-mutate
Prérequis : Slither v0.10.2+.
- Lister les options et les mutators :
slither-mutate --help
slither-mutate --list-mutators
- Exemple avec Foundry (capturer les résultats et conserver un journal complet):
slither-mutate ./src/contracts --test-cmd="forge test" &> >(tee mutation.results)
- Si vous n'utilisez pas Foundry, remplacez
--test-cmdpar la commande que vous utilisez pour exécuter les tests (p. ex.,npx hardhat test,npm test).
Les artifacts et rapports sont stockés dans ./mutation_campaign par défaut. Les mutants non interceptés (survivants) y sont copiés pour inspection.
Comprendre la sortie
Les lignes du rapport ressemblent Ă :
INFO:Slither-Mutate:Mutating contract ContractName
INFO:Slither-Mutate:[CR] Line 123: 'original line' ==> '//original line' --> UNCAUGHT
- Le tag entre crochets est l'alias du mutator (e.g.,
CR= Comment Replacement). UNCAUGHTsignifie que les tests sont passĂ©s sous le comportement mutĂ© â assertion manquante.
Reducing runtime: prioritize impactful mutants
Les campagnes de mutation peuvent prendre des heures ou des jours. Conseils pour réduire le coût :
- Scope : Commencez par les contrats/répertoires critiques uniquement, puis étendez.
- Prioritize mutators : si un mutant à haute priorité sur une ligne survit (p.ex., ligne entiÚre commentée), vous pouvez ignorer les variantes de moindre priorité pour cette ligne.
- Parallelize tests si votre runner le permet ; mettez en cache dependencies/builds.
- Fail-fast : arrĂȘtez tĂŽt lorsqu'un changement met clairement en Ă©vidence une faille d'assertion.
Triage workflow for surviving mutants
- Inspect the mutated line and behavior.
- Reproduisez localement en appliquant la ligne mutée et en exécutant un test ciblé.
- Strengthen tests to assert state, not only return values.
- Ajoutez des vérifications de bornes d'égalité (p.ex., vérifier le seuil
==). - Affirmez les post-conditions : soldes, offre totale, effets d'autorisation, et événements émis.
- Replace overly permissive mocks with realistic behavior.
- Assurez-vous que les mocks imposent les transferts, les chemins d'échec, et les émissions d'événements qui se produisent on-chain.
- Add invariants for fuzz tests.
- Ex. conservation de la valeur, soldes non négatifs, invariants d'autorisation, offre monotone lorsque applicable.
- Re-run slither-mutate until survivors are killed or explicitly justified.
Case study: revealing missing state assertions (Arkis protocol)
Une campagne de mutation lors d'un audit du Arkis DeFi protocol a mis en évidence des survivants tels que :
INFO:Slither-Mutate:[CR] Line 33: 'cmdsToExecute.last().value = _cmd.value' ==> '//cmdsToExecute.last().value = _cmd.value' --> UNCAUGHT
Commenter l'assignation n'a pas cassé les tests, prouvant l'absence d'assertions sur l'état final. Cause racine : le code faisait confiance à _cmd.value contrÎlé par l'utilisateur au lieu de valider les transferts de tokens réels. Un attaquant pouvait désynchroniser les transferts attendus et réels pour vider des fonds. Conséquence : risque de haute gravité pour la solvabilité du protocole.
Conseil : Traitez les mutants survivants qui affectent les transferts de valeur, la comptabilité ou le contrÎle d'accÚs comme à haut risque tant qu'ils ne sont pas éliminés.
Checklist pratique
- Lancez une campagne ciblée :
slither-mutate ./src/contracts --test-cmd="forge test"- Trier les survivants et écrire des tests/invariants qui échoueraient avec le comportement muté.
- Vérifiez les soldes, l'offre totale, les autorisations et les événements.
- Ajoutez des tests limites (
==, overflows/underflows, zero-address, zero-amount, empty arrays). - Remplacez les mocks irréalistes ; simulez des modes de défaillance.
- Itérez jusqu'à ce que tous les mutants soient éliminés ou justifiés par des commentaires et une explication.
References
- Use mutation testing to find the bugs your tests don't catch (Trail of Bits)
- Arkis DeFi Prime Brokerage Security Review (Appendix C)
- Slither (GitHub)
tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :
HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le đŹ groupe Discord ou le groupe telegram ou suivez-nous sur Twitter đŠ @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépÎts github.
HackTricks