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

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 :

solidity
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 :
bash
slither-mutate --help
slither-mutate --list-mutators
  • Exemple avec Foundry (capturer les rĂ©sultats et conserver un journal complet):
bash
slither-mutate ./src/contracts --test-cmd="forge test" &> >(tee mutation.results)
  • Si vous n'utilisez pas Foundry, remplacez --test-cmd par 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 Ă  :

text
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).
  • UNCAUGHT signifie 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

  1. Inspect the mutated line and behavior.
  • Reproduisez localement en appliquant la ligne mutĂ©e et en exĂ©cutant un test ciblĂ©.
  1. 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.
  1. 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.
  1. Add invariants for fuzz tests.
  • Ex. conservation de la valeur, soldes non nĂ©gatifs, invariants d'autorisation, offre monotone lorsque applicable.
  1. 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 :

text
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

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