hacktricks/src/blockchain/smart-contract-security/mutation-testing-with-slither.md

6.0 KiB
Raw Blame History

Mutation Testing za Solidity sa Slither (slither-mutate)

{{#include ../../banners/hacktricks-training.md}}

Mutation testing "tests your tests" tako što sistematski uvodi male promene (mutants) u vaš Solidity код и поново покреће ваш test suite. Ако тест не успе, mutant је killed. Ако тестови и даље пролазе, mutant преживи, откривајући слепу тачку у вашем test suite коју line/branch coverage не може да детектује.

Key idea: Coverage показује да је код извршен; mutation testing показује да ли је понашање заиста потврђено.

Zašto coverage може зaварaти

Размотрите ову једноставну проверу прага:

function verifyMinimumDeposit(uint256 deposit) public returns (bool) {
if (deposit >= 1 ether) {
return true;
} else {
return false;
}
}

Jedinični testovi koji proveravaju samo vrednost ispod i vrednost iznad praga mogu dostići 100% pokrivenost linija/grana, a ipak ne potvrditi granicu jednakosti (==). Refaktorisanje u deposit >= 2 ether i dalje bi prošlo takve testove, neprimetno narušivši logiku protokola.

Mutaciono testiranje otkriva ovaj nedostatak mutiranjem uslova i proverom da li testovi zakažu.

Uobičajeni mutacioni operatori za Solidity

Slither-ov mehanizam za mutacije primenjuje mnoge male izmene koje menjaju semantiku, kao što su:

  • Zamena operatora: +-, */, etc.
  • Zamena dodele: +==, -==
  • Zamena konstanti: nenulta → 0, truefalse
  • Negacija/zamena uslova unutar if/petlji
  • Zakomentarisati cele linije (CR: Comment Replacement)
  • Zameniti liniju sa revert()
  • Zamena tipova podataka: npr. int128int64

Cilj: eliminisati 100% generisanih mutanata, ili opravdati preživele jasnim obrazloženjem.

Pokretanje mutacionog testiranja sa slither-mutate

Zahtevi: Slither v0.10.2+.

  • Prikaži opcije i mutatore:
slither-mutate --help
slither-mutate --list-mutators
  • Foundry primer (zabeleži rezultate i vodi kompletan log):
slither-mutate ./src/contracts --test-cmd="forge test" &> >(tee mutation.results)
  • Ako ne koristite Foundry, zamenite --test-cmd načinom na koji pokrećete testove (npr. npx hardhat test, npm test).

Artefakti i izveštaji se podrazumevano čuvaju u ./mutation_campaign. Neotkriveni (preživeli) mutanti se tamo kopiraju radi inspekcije.

Razumevanje izlaza

Linije izveštaja izgledaju ovako:

INFO:Slither-Mutate:Mutating contract ContractName
INFO:Slither-Mutate:[CR] Line 123: 'original line' ==> '//original line' --> UNCAUGHT
  • Oznaka u uglastim zagradama je alias mutatora (npr., CR = Comment Replacement).
  • UNCAUGHT znači da su testovi prošli pod mutiranim ponašanjem → nedostaje asercija.

Smanjivanje vremena izvršavanja: prioritizujte mutante sa najvećim uticajem

Kampanje mutacije mogu trajati satima ili danima. Saveti za smanjenje troškova:

  • Opseg: Počnite samo sa kritičnim ugovorima/direktorijumima, pa zatim proširite.
  • Prioritetizujte mutatore: Ako mutant visokog prioriteta na liniji preživi (npr., cela linija je zakomentarisana), možete preskočiti varijante nižeg prioriteta za tu liniju.
  • Paralelizujte testove ako vaš runner to omogućava; keširajte zavisnosti i build-ove.
  • Fail-fast: zaustavite se rano kada promena jasno demonstrira prazninu u asercijama.

Radni tok trijaže za mutante koji prežive

  1. Pregledajte mutiranu liniju i ponašanje.
  • Reproducirajte lokalno primenom mutirane linije i pokretanjem fokusiranog testa.
  1. Ojačajte testove tako da proveravaju stanje, a ne samo povratne vrednosti.
  • Dodajte provere granica jednakosti (npr., test threshold ==).
  • Proverite post-uslove: bilansi, total supply, efekti autorizacije i emitovani događaji.
  1. Zamenite previše permisivne mock-ove realističnim ponašanjem.
  • Osigurajte da mock-ovi nameću transfere, puteve greške i emitovanje događaja koji se dešavaju on-chain.
  1. Dodajte invarijante za fuzz testove.
  • Npr., očuvanje vrednosti, nenegativni saldi, invarijante autorizacije, monotonost supply-a gde je primenljivo.
  1. Ponovo pokrenite slither-mutate dok preživeli ne budu uklonjeni ili dok se eksplicitno ne opravdaju.

Studija slučaja: otkrivanje nedostajućih asercija stanja (Arkis protocol)

Kampanja mutacije tokom audita Arkis DeFi protocola je identifikovala preživele, kao što su:

INFO:Slither-Mutate:[CR] Line 33: 'cmdsToExecute.last().value = _cmd.value' ==> '//cmdsToExecute.last().value = _cmd.value' --> UNCAUGHT

Komentarisanje dodele nije pokvarilo testove, što ukazuje na nedostatak post-state assertions. Osnovni uzrok: kod je verovao korisnički kontrolisanom _cmd.value umesto da verifikuje stvarne transfere tokena. Napadač bi mogao desinhronizovati očekivane i stvarne transfere kako bi isisao sredstva. Posledica: visok rizik po solventnost protokola.

Smernice: Smatrajte survivors koji utiču na value transfers, accounting, ili access control visokorizičnim dok nisu killed.

Praktična kontrolna lista

  • Pokrenite ciljanu kampanju:
  • slither-mutate ./src/contracts --test-cmd="forge test"
  • Razvrstajte survivors i napišite testove/invarijante koje bi pale pri mutiranom ponašanju.
  • Proverite balances, supply, authorizations i events.
  • Dodajte boundary testove (==, overflows/underflows, zero-address, zero-amount, empty arrays).
  • Zamenite nerealne mocks; simulirajte failure modes.
  • Iterirajte dok svi mutants ne budu killed ili opravdani komentarima i objašnjenjem.

References

{{#include ../../banners/hacktricks-training.md}}