Add human readable printer (WIP)

pull/13/head
Josselin 6 years ago
parent f18f8ee260
commit 07de8cb9ef
  1. 12
      slither/core/declarations/contract.py
  2. 1
      slither/printers/printers.py
  3. 107
      slither/printers/summary/printer_human_summary.py
  4. 75
      slither/utils/code_complexity.py

@ -287,6 +287,18 @@ class Contract(ChildSlither, SourceMapping):
"""
return next((e for e in self.enums if e.canonical_name == enum_name), None)
def is_erc20(self):
"""
Check if the contract is a erc20 token
Note: it does not check for correct return values
Returns:
bool
"""
full_names = [f.full_name for f in self.functions]
return 'transfer(address,uint256)' in full_names and\
'transferFrom(address,address,uint256)' in full_names and\
'approve(address,uint256)' in full_names
def get_summary(self):
""" Return the function summary

@ -6,6 +6,7 @@ from slither.printers.abstractPrinter import AbstractPrinter
# Printer must be imported here
from slither.printers.summary.printerSummary import PrinterSummary
from slither.printers.summary.printerQuickSummary import PrinterQuickSummary
from slither.printers.summary.printer_human_summary import PrinterHumanSummary
from slither.printers.inheritance.printerInheritance import PrinterInheritance
from slither.printers.functions.authorization import PrinterWrittenVariablesAndAuthorization

@ -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…
Cancel
Save