From 7045cb82d88adeaddd2c04947d7a9a315bc7b1cf Mon Sep 17 00:00:00 2001 From: Alexis Date: Mon, 8 Apr 2024 18:31:19 +0200 Subject: [PATCH] Update Cheatcode Printer --- slither/printers/all_printers.py | 1 + slither/printers/summary/cheatcodes.py | 55 ++++++++++++++- .../test_printer_cheatcode/README.md | 68 ++----------------- .../test_printer_cheatcode/src/Counter.sol | 14 ++++ .../test_printer_cheatcode/src/Vault.sol | 8 --- .../test_printer_cheatcode/test/Counter.t.sol | 36 ++++++++++ .../test_printer_cheatcode/test/Vault.t.sol | 32 --------- tests/e2e/printers/test_printers.py | 15 +++- 8 files changed, 121 insertions(+), 108 deletions(-) create mode 100644 tests/e2e/printers/test_data/test_printer_cheatcode/src/Counter.sol delete mode 100644 tests/e2e/printers/test_data/test_printer_cheatcode/src/Vault.sol create mode 100644 tests/e2e/printers/test_data/test_printer_cheatcode/test/Counter.t.sol delete mode 100644 tests/e2e/printers/test_data/test_printer_cheatcode/test/Vault.t.sol diff --git a/slither/printers/all_printers.py b/slither/printers/all_printers.py index 3edd5325b..034578f52 100644 --- a/slither/printers/all_printers.py +++ b/slither/printers/all_printers.py @@ -24,3 +24,4 @@ from .summary.when_not_paused import PrinterWhenNotPaused from .summary.declaration import Declaration from .functions.dominator import Dominator from .summary.martin import Martin +from .summary.cheatcodes import CheatcodePrinter diff --git a/slither/printers/summary/cheatcodes.py b/slither/printers/summary/cheatcodes.py index 8c84da8e6..50dcdc158 100644 --- a/slither/printers/summary/cheatcodes.py +++ b/slither/printers/summary/cheatcodes.py @@ -4,6 +4,7 @@ This printer prints the usage of cheatcode in the code. """ from slither.printers.abstract_printer import AbstractPrinter +from slither.slithir.operations import HighLevelCall from slither.utils import output @@ -11,9 +12,59 @@ class CheatcodePrinter(AbstractPrinter): ARGUMENT = "cheatcode" - HELP = """""" + HELP = """ + Print the usage of (Foundry) cheatcodes in the code. + For the complete list of Cheatcodes, see https://book.getfoundry.sh/cheatcodes/ + """ WIKI = "https://github.com/trailofbits/slither/wiki/Printer-documentation#cheatcode" def output(self, filename: str) -> output.Output: - pass + + info: str = "" + + vm = self.slither.get_contract_from_name("Vm").pop() + + for contract in self.slither.contracts_derived: + # Check that the IS_TEST variable is set. (Only works for Foundry) + is_test_var = contract.variables_as_dict.get("IS_TEST", None) + is_test = False + if is_test_var is not None: + try: + is_test = is_test_var.expression.value == "true" + except AttributeError: + pass + + if not is_test: + continue + + found_contract: bool = False + contract_info: str = "" + for func in contract.functions_declared: + function_info = f"\t{func}\n" + found_function: bool = False + for node in func.nodes: + for op in node.all_slithir_operations(): + if ( + isinstance(op, HighLevelCall) + and op.function.contract == vm + and op.function.visibility == "external" + ): + found_function = True + function_info += ( + f"\t\tL{node.source_mapping.lines}: {op.function.name}\n" + ) + + if found_function: + if found_contract is False: + contract_info = f"{contract} ({contract.source_mapping.filename.short})\n" + found_contract = True + + contract_info += function_info + + if found_contract: + info += contract_info + + self.info(info) + res = output.Output(info) + return res diff --git a/tests/e2e/printers/test_data/test_printer_cheatcode/README.md b/tests/e2e/printers/test_data/test_printer_cheatcode/README.md index 9265b4558..71bbaff38 100644 --- a/tests/e2e/printers/test_data/test_printer_cheatcode/README.md +++ b/tests/e2e/printers/test_data/test_printer_cheatcode/README.md @@ -1,66 +1,6 @@ -## Foundry - -**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** - -Foundry consists of: - -- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). -- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. -- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. -- **Chisel**: Fast, utilitarian, and verbose solidity REPL. - -## Documentation - -https://book.getfoundry.sh/ - -## Usage - -### Build - -```shell -$ forge build -``` - -### Test - -```shell -$ forge test -``` - -### Format - -```shell -$ forge fmt -``` - -### Gas Snapshots - -```shell -$ forge snapshot -``` - -### Anvil - -```shell -$ anvil -``` - -### Deploy - -```shell -$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key -``` - -### Cast - -```shell -$ cast -``` - -### Help +# Counter +Init using : ```shell -$ forge --help -$ anvil --help -$ cast --help -``` +forge install foundry-rs/forge-std +``` \ No newline at end of file diff --git a/tests/e2e/printers/test_data/test_printer_cheatcode/src/Counter.sol b/tests/e2e/printers/test_data/test_printer_cheatcode/src/Counter.sol new file mode 100644 index 000000000..aded7997b --- /dev/null +++ b/tests/e2e/printers/test_data/test_printer_cheatcode/src/Counter.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} diff --git a/tests/e2e/printers/test_data/test_printer_cheatcode/src/Vault.sol b/tests/e2e/printers/test_data/test_printer_cheatcode/src/Vault.sol deleted file mode 100644 index 2aaf6653c..000000000 --- a/tests/e2e/printers/test_data/test_printer_cheatcode/src/Vault.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; - -contract Vault { - constructor(){ - - } -} diff --git a/tests/e2e/printers/test_data/test_printer_cheatcode/test/Counter.t.sol b/tests/e2e/printers/test_data/test_printer_cheatcode/test/Counter.t.sol new file mode 100644 index 000000000..529a7ce08 --- /dev/null +++ b/tests/e2e/printers/test_data/test_printer_cheatcode/test/Counter.t.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import "../src/Counter.sol"; + +contract CounterTest is Test { + Counter public counter; + + address public alice = address(0x42); + address public bob = address(0x43); + + function difficulty(uint256 value) public { + // empty + } + + function setUp() public { + counter = new Counter(); + counter.setNumber(0); + + vm.deal(alice, 1 ether); + vm.deal(bob, 2 ether); + + difficulty(1); + } + + function testIncrement() public { + vm.prank(alice); + counter.increment(); + assertEq(counter.number(), 1); + + vm.prank(bob); + counter.increment(); + assertEq(counter.number(), 2); + } +} diff --git a/tests/e2e/printers/test_data/test_printer_cheatcode/test/Vault.t.sol b/tests/e2e/printers/test_data/test_printer_cheatcode/test/Vault.t.sol deleted file mode 100644 index 6a598136d..000000000 --- a/tests/e2e/printers/test_data/test_printer_cheatcode/test/Vault.t.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; - -import "forge-std/Test.sol"; -import "../src/Vault.sol"; - -contract VaultTest is Test { - - Vault public vault; - - address public alice = address(0x42); - - function broadcast() pure public {} - - function setUp() public { - vm.prank(alice); - vm.deal(alice, 1000 ether); - - uint256 value = 123; - - vm.warp(1641070800); - vm.roll(100); - vm.fee(25 gwei); - - // Not a cheatcode - broadcast(); - } - - function test_deal() public { - - } -} diff --git a/tests/e2e/printers/test_printers.py b/tests/e2e/printers/test_printers.py index 8d9c5587c..121c33035 100644 --- a/tests/e2e/printers/test_printers.py +++ b/tests/e2e/printers/test_printers.py @@ -1,14 +1,15 @@ import re import shutil -import pytest from collections import Counter from pathlib import Path +import pytest from crytic_compile import CryticCompile from crytic_compile.platform.solc_standard_json import SolcStandardJson from slither import Slither from slither.printers.inheritance.inheritance_graph import PrinterInheritanceGraph +from slither.printers.summary.cheatcodes import CheatcodePrinter TEST_DATA_DIR = Path(__file__).resolve().parent / "test_data" @@ -45,4 +46,14 @@ def test_inheritance_printer(solc_binary_path) -> None: not foundry_available or not project_ready, reason="requires Foundry and project setup" ) def test_printer_cheatcode(): - slither = Slither(Path(TEST_DATA_DIR, "test_printer_cheatcode").as_posix()) \ No newline at end of file + slither = Slither( + Path(TEST_DATA_DIR, "test_printer_cheatcode").as_posix(), foundry_compile_all=True + ) + + printer = CheatcodePrinter(slither=slither, logger=None) + output = printer.output("cheatcode.out") + + assert ( + output + == "CounterTest (test/Counter.t.sol)\n\tsetUp\n\t\tL[21]: deal\n\t\tL[22]: deal\n\ttestIncrement\n\t\tL[28]: prank\n\t\tL[30]: assertEq\n\t\tL[32]: prank\n\t\tL[34]: assertEq\n" + )