Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Printer cheatcode #2413

Merged
merged 10 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion scripts/ci_test_printers.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
cd tests/e2e/solc_parsing/test_data/compile/ || exit

# Do not test the evm printer,as it needs a refactoring
ALL_PRINTERS="cfg,constructor-calls,contract-summary,data-dependency,echidna,function-id,function-summary,modifiers,call-graph,halstead,human-summary,inheritance,inheritance-graph,loc,martin,slithir,slithir-ssa,vars-and-auth,require,variable-order,declaration,ck"
ALL_PRINTERS="cfg,constructor-calls,contract-summary,data-dependency,echidna,function-id,function-summary,modifiers,call-graph,halstead,human-summary,inheritance,inheritance-graph,loc,martin,slithir,slithir-ssa,vars-and-auth,require,variable-order,declaration,ck,cheatcode"

# Only test 0.5.17 to limit test time
for file in *0.5.17-compact.zip; do
Expand Down
1 change: 1 addition & 0 deletions slither/printers/all_printers.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@
from .summary.declaration import Declaration
from .functions.dominator import Dominator
from .summary.martin import Martin
from .summary.cheatcodes import CheatcodePrinter
73 changes: 73 additions & 0 deletions slither/printers/summary/cheatcodes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"""
Cheatcode printer

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


class CheatcodePrinter(AbstractPrinter):

ARGUMENT = "cheatcode"

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:

info: str = ""

try:
vm = self.slither.get_contract_from_name("Vm").pop()
except IndexError:
return output.Output("No contract named VM found")

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
DarkaMaul marked this conversation as resolved.
Show resolved Hide resolved

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"
)
DarkaMaul marked this conversation as resolved.
Show resolved Hide resolved

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
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Counter
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct the spelling error in the comment.

- # Counter
+ # Counter - Initialize using:

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
# Counter
# Counter - Initialize using:


Init using :
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarify the initialization instruction for better readability.

- Init using :
+ Initialize the environment using the following command:

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
Init using :
Initialize the environment using the following command:

```shell
forge install foundry-rs/forge-std
```
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure fenced code blocks are surrounded by blank lines for proper formatting.

+ 
```shell
5~
forge install foundry-rs/forge-std
6~

<!-- This is an auto-generated comment by CodeRabbit -->

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a newline at the end of the file to adhere to best practices.

+

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.

Suggested change
```

</details>
<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[profile.default]
src = 'src'
out = 'out'
libs = ['lib']

# See more config options https://github.com/foundry-rs/foundry/tree/master/config
Original file line number Diff line number Diff line change
@@ -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++;
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
23 changes: 23 additions & 0 deletions tests/e2e/printers/test_printers.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import re
import shutil
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"

foundry_available = shutil.which("forge") is not None
project_ready = Path(TEST_DATA_DIR, "test_printer_cheatcode/lib/forge-std").exists()


def test_inheritance_printer(solc_binary_path) -> None:
solc_path = solc_binary_path("0.8.0")
Expand All @@ -34,3 +40,20 @@ def test_inheritance_printer(solc_binary_path) -> None:

assert counter["B -> A"] == 2
assert counter["C -> A"] == 1


@pytest.mark.skipif(
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(), foundry_compile_all=True
)

printer = CheatcodePrinter(slither=slither, logger=None)
output = printer.output("cheatcode.out")

assert (
output.data["description"]
== "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"
)
Loading