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

Add entry-points printer to identify all externally accessible state-changing functions #2616

Merged
merged 10 commits into from
Feb 3, 2025
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,10 +232,11 @@ For more information, see

### Quick Review Printers

* `human-summary`: [Print a human-readable summary of the contracts](https://github.com/crytic/slither/wiki/Printer-documentation#human-summary)
* `inheritance-graph`: [Export the inheritance graph of each contract to a dot file](https://github.com/crytic/slither/wiki/Printer-documentation#inheritance-graph)
* `contract-summary`: [Print a summary of the contracts](https://github.com/crytic/slither/wiki/Printer-documentation#contract-summary)
* `loc`: [Count the total number lines of code (LOC), source lines of code (SLOC), and comment lines of code (CLOC) found in source files (SRC), dependencies (DEP), and test files (TEST).](https://github.com/crytic/slither/wiki/Printer-documentation#loc)
* `human-summary`: [Print a human-readable summary of the contracts](https://github.com/trailofbits/slither/wiki/Printer-documentation#human-summary)
* `inheritance-graph`: [Export the inheritance graph of each contract to a dot file](https://github.com/trailofbits/slither/wiki/Printer-documentation#inheritance-graph)
* `contract-summary`: [Print a summary of the contracts](https://github.com/trailofbits/slither/wiki/Printer-documentation#contract-summary)
* `loc`: [Count the total number lines of code (LOC), source lines of code (SLOC), and comment lines of code (CLOC) found in source files (SRC), dependencies (DEP), and test files (TEST).](https://github.com/trailofbits/slither/wiki/Printer-documentation#loc)
* `entry-points`: [Print all the state-changing entry point functions of the contracts](https://github.com/trailofbits/slither/wiki/Printer-documentation#entry-points)

### In-Depth Review Printers

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 @@ -25,3 +25,4 @@
from .functions.dominator import Dominator
from .summary.martin import Martin
from .summary.cheatcodes import CheatcodePrinter
from .summary.entry_points import PrinterEntryPoints
94 changes: 94 additions & 0 deletions slither/printers/summary/entry_points.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"""
Module printing all the state-changing entry point functions of the contracts
"""

from slither.printers.abstract_printer import AbstractPrinter
from slither.core.declarations.function_contract import FunctionContract
from slither.utils.colors import Colors
from slither.utils.output import Output
from slither.utils.myprettytable import MyPrettyTable


class PrinterEntryPoints(AbstractPrinter):

ARGUMENT = "entry-points"
HELP = "Print all the state-changing entry point functions of the contracts"

WIKI = "https://github.com/trailofbits/slither/wiki/Printer-documentation#entry-points"

def output(self, _filename) -> Output:
"""
_filename is not used
Args:
_filename(string)
"""
all_contracts = []

for contract in sorted(
(
c
for c in self.contracts
if not c.is_interface
and not c.is_library
and not c.is_abstract
and "lib/" not in c.source_mapping.filename.absolute
and "node_modules/" not in c.source_mapping.filename.absolute
and not any(
mock in c.source_mapping.filename.absolute.lower() for mock in ["mock", "mocks"]
)
),
key=lambda x: x.name,
):
entry_points = [
f
for f in contract.functions
if (
f.visibility in ["public", "external"]
and isinstance(f, FunctionContract)
and not f.is_constructor
and not f.view
and not f.pure
and not f.contract_declarer.is_interface
and not f.contract_declarer.is_library
and not f.is_shadowed
)
]

if not entry_points:
continue

table = MyPrettyTable(["Function", "Modifiers", "Inherited From"])
contract_info = [
f"\nContract {Colors.BOLD}{Colors.YELLOW}{contract.name}{Colors.END}"
f" ({contract.source_mapping})"
]

for f in sorted(
entry_points,
key=lambda x: (x.visibility != "external", x.visibility != "public", x.full_name),
):
modifier_list = [m.name for m in f.modifiers]
if f.payable:
modifier_list.append("payable")
modifiers = ", ".join(modifier_list) if modifier_list else ""
inherited = f"{f.contract_declarer.name}" if f.contract_declarer != contract else ""

name_parts = f.full_name.split("(", 1)
function_name = (
f"{Colors.BOLD}{Colors.RED}{name_parts[0]}{Colors.END}" f"({name_parts[1]}"
)

table.add_row(
[
function_name,
f"{Colors.GREEN}{modifiers}{Colors.END}" if modifiers else "",
f"{Colors.MAGENTA}{inherited}{Colors.END}" if inherited else "",
]
)

contract_info.append(str(table))
all_contracts.append("\n".join(contract_info))

info = "\n".join(all_contracts) if all_contracts else ""
self.info(info)
return self.generate_output(info)