mirror of https://github.com/crytic/slither
parent
f18f8ee260
commit
07de8cb9ef
@ -0,0 +1,107 @@ |
||||
""" |
||||
Module printing summary of the contract |
||||
""" |
||||
import logging |
||||
|
||||
from slither.printers.abstractPrinter import AbstractPrinter |
||||
from slither.utils.colors import blue, green, magenta, red, yellow |
||||
|
||||
from slither.utils.code_complexity import compute_cyclomatic_complexity |
||||
|
||||
from slither.detectors.detectors import Detectors |
||||
|
||||
class PrinterHumanSummary(AbstractPrinter): |
||||
|
||||
ARGUMENT = 'print-human-summary' |
||||
HELP = 'Print an human readable summary of the contracts' |
||||
|
||||
@staticmethod |
||||
def get_summary_erc20(contract): |
||||
txt = '' |
||||
functions_name = [f.name for f in contract.functions] |
||||
state_variables = [v.name for v in contract.state_variables] |
||||
|
||||
if 'paused' in functions_name or 'pause' in functions_name: |
||||
txt += "\t\t Can be paused? : {}\n".format(yellow('Yes')) |
||||
else: |
||||
txt += "\t\t Can be paused? : {}\n".format(green('No')) |
||||
|
||||
if 'mint' in functions_name: |
||||
if not 'mintingFinished' in state_variables: |
||||
txt += "\t\t Minting restriction? : {}\n".format(red('None')) |
||||
else: |
||||
txt += "\t\t Minting restriction? : {}\n".format(yellow('Yes')) |
||||
else: |
||||
txt += "\t\t Minting restriction? : {}\n".format(green('No Minting')) |
||||
|
||||
if 'increaseApproval' in functions_name: |
||||
txt += "\t\t ERC20 race condition mitigation: {}\n".format(green('Yes')) |
||||
else: |
||||
txt += "\t\t ERC20 race condition mitigation: {}\n".format(red('No')) |
||||
|
||||
return txt |
||||
|
||||
def get_detectors_result(self): |
||||
detectors = Detectors() |
||||
|
||||
# disable detectors logger |
||||
logger = logging.getLogger('Detectors') |
||||
logger.setLevel(logging.ERROR) |
||||
|
||||
checks_low = detectors.low |
||||
checks_medium = detectors.medium |
||||
checks_high = detectors.high |
||||
|
||||
issues_low = [detectors.run_detector(self.slither, c) for c in checks_low] |
||||
issues_low = [c for c in issues_low if c] |
||||
issues_medium = [detectors.run_detector(self.slither, c) for c in checks_medium] |
||||
issues_medium = [c for c in issues_medium if c] |
||||
issues_high = [detectors.run_detector(self.slither, c) for c in checks_high] |
||||
issues_high = [c for c in issues_high if c] |
||||
|
||||
txt = "Number of low issue: {}\n".format(green(str(len(issues_low)))) |
||||
txt += "Number of medium issue: {}\n".format(yellow(str(len(issues_medium)))) |
||||
txt += "Number of high issue: {}\n".format(red(str(len(issues_high)))) |
||||
|
||||
return txt |
||||
|
||||
@staticmethod |
||||
def is_complex_code(contract): |
||||
""" |
||||
Check if the code is complex |
||||
Heuristic, the code is complex if: |
||||
- One function has a cyclomatic complexity > 7 |
||||
Args: |
||||
contract |
||||
""" |
||||
is_complex = False |
||||
|
||||
for f in contract.functions: |
||||
if compute_cyclomatic_complexity(f) > 7: |
||||
is_complex = True |
||||
|
||||
if is_complex: |
||||
txt = "\tComplex code? {}\n".format(red('Yes')) |
||||
else: |
||||
txt = "\tComplex code? {}\n".format(green('No')) |
||||
|
||||
return txt |
||||
|
||||
def output(self, _filename): |
||||
""" |
||||
_filename is not used |
||||
Args: |
||||
_filename(string) |
||||
""" |
||||
|
||||
txt = "Analyze of {}\n".format(self.slither.filename) |
||||
txt += self.get_detectors_result() |
||||
for contract in self.slither.contracts_derived: |
||||
txt += "\nContract {}\n".format(contract.name) |
||||
txt += self.is_complex_code(contract) |
||||
is_erc20 = contract.is_erc20() |
||||
txt += "\tIs ERC20 token: {}\n".format(contract.is_erc20()) |
||||
if is_erc20: |
||||
txt += self.get_summary_erc20(contract) |
||||
|
||||
self.info(txt) |
@ -0,0 +1,75 @@ |
||||
# Funciton computing the code complexity |
||||
|
||||
def compute_number_edges(function): |
||||
""" |
||||
Compute the number of edges of the CFG |
||||
Args: |
||||
function (core.declarations.function.Function) |
||||
Returns: |
||||
int |
||||
""" |
||||
n = 0 |
||||
for node in function.nodes: |
||||
n += len(node.sons) |
||||
return n |
||||
|
||||
|
||||
def compute_strongly_connected_components(function): |
||||
""" |
||||
Compute strongly connected components |
||||
Based on Kosaraju algo |
||||
Implem follows wikipedia algo: https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm#The_algorithm |
||||
Args: |
||||
function (core.declarations.function.Function) |
||||
Returns: |
||||
list(list(nodes)) |
||||
""" |
||||
visited = {n:False for n in function.nodes} |
||||
assigned = {n:False for n in function.nodes} |
||||
components = [] |
||||
l = [] |
||||
|
||||
def visit(node): |
||||
if not visited[node]: |
||||
visited[node] = True |
||||
for son in node.sons: |
||||
visit(son) |
||||
l.append(node) |
||||
|
||||
for n in function.nodes: |
||||
visit(n) |
||||
|
||||
def assign(node, root): |
||||
if not assigned[node]: |
||||
assigned[node] = True |
||||
root.append(node) |
||||
for father in node.fathers: |
||||
assign(father, root) |
||||
|
||||
for n in l: |
||||
component = [] |
||||
assign(n, component) |
||||
if component: |
||||
components.append(component) |
||||
|
||||
return components |
||||
|
||||
def compute_cyclomatic_complexity(function): |
||||
""" |
||||
Compute the cyclomatic complexity of a function |
||||
Args: |
||||
function (core.declarations.function.Function) |
||||
Returns: |
||||
int |
||||
""" |
||||
# from https://en.wikipedia.org/wiki/Cyclomatic_complexity |
||||
# M = E - N + 2P |
||||
# where M is the complexity |
||||
# E number of edges |
||||
# N number of nodes |
||||
# P number of connected components |
||||
|
||||
E = compute_number_edges(function) |
||||
N = len(function.nodes) |
||||
P = len(compute_strongly_connected_components(function)) |
||||
return E - N + 2 * P |
Loading…
Reference in new issue