Merge branch 'dev-printer-human-readable' into dev

pull/81/head
Josselin 6 years ago
commit 95b09cc708
  1. 18
      slither/__main__.py
  2. 3
      slither/core/declarations/contract.py
  3. 105
      slither/printers/summary/human_summary.py
  4. 22
      slither/slither.py
  5. 4
      slither/utils/code_complexity.py
  6. 22
      slither/utils/colors.py

@ -46,11 +46,12 @@ def _process(slither, detector_classes, printer_classes):
results = [] results = []
detector_results = slither.run_detectors() if not printer_classes:
detector_results = [x for x in detector_results if x] # remove empty results detector_results = slither.run_detectors()
detector_results = [item for sublist in detector_results for item in sublist] # flatten detector_results = [x for x in detector_results if x] # remove empty results
detector_results = [item for sublist in detector_results for item in sublist] # flatten
results.extend(detector_results) results.extend(detector_results)
slither.run_printers() # Currently printers does not return results slither.run_printers() # Currently printers does not return results
@ -137,6 +138,7 @@ def get_detectors_and_printers():
from slither.printers.call.call_graph import PrinterCallGraph from slither.printers.call.call_graph import PrinterCallGraph
from slither.printers.functions.authorization import PrinterWrittenVariablesAndAuthorization from slither.printers.functions.authorization import PrinterWrittenVariablesAndAuthorization
from slither.printers.summary.slithir import PrinterSlithIR from slither.printers.summary.slithir import PrinterSlithIR
from slither.printers.summary.human_summary import PrinterHumanSummary
printers = [FunctionSummary, printers = [FunctionSummary,
ContractSummary, ContractSummary,
@ -144,7 +146,8 @@ def get_detectors_and_printers():
PrinterInheritanceGraph, PrinterInheritanceGraph,
PrinterCallGraph, PrinterCallGraph,
PrinterWrittenVariablesAndAuthorization, PrinterWrittenVariablesAndAuthorization,
PrinterSlithIR] PrinterSlithIR,
PrinterHumanSummary]
# Handle plugins! # Handle plugins!
for entry_point in iter_entry_points(group='slither_analyzer.plugin', name=None): for entry_point in iter_entry_points(group='slither_analyzer.plugin', name=None):
@ -178,10 +181,7 @@ def main_impl(all_detector_classes, all_printer_classes):
args = parse_args(all_detector_classes, all_printer_classes) args = parse_args(all_detector_classes, all_printer_classes)
printer_classes = choose_printers(args, all_printer_classes) printer_classes = choose_printers(args, all_printer_classes)
if printer_classes: detector_classes = choose_detectors(args, all_detector_classes)
detector_classes = []
else:
detector_classes = choose_detectors(args, all_detector_classes)
default_log = logging.INFO if not args.debug else logging.DEBUG default_log = logging.INFO if not args.debug else logging.DEBUG

@ -302,7 +302,8 @@ class Contract(ChildSlither, SourceMapping):
def is_erc20(self): def is_erc20(self):
""" """
Check if the contract is a erc20 token Check if the contract is an erc20 token
Note: it does not check for correct return values Note: it does not check for correct return values
Returns: Returns:
bool bool

@ -0,0 +1,105 @@
"""
Module printing summary of the contract
"""
import logging
from slither.printers.abstract_printer import AbstractPrinter
from slither.utils.code_complexity import compute_cyclomatic_complexity
from slither.utils.colors import green, red, yellow
class PrinterHumanSummary(AbstractPrinter):
ARGUMENT = 'human-summary'
HELP = 'Print a 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 '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 or 'safeIncreaseAllowance' 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):
# disable detectors logger
logger = logging.getLogger('Detectors')
logger.setLevel(logging.ERROR)
checks_informational = self.slither.detectors_informational
checks_low = self.slither.detectors_low
checks_medium = self.slither.detectors_medium
checks_high = self.slither.detectors_high
issues_informational = [c.detect() for c in checks_informational]
issues_informational = [item for sublist in issues_informational for item in sublist]
issues_low = [c.detect() for c in checks_low]
issues_low = [c for c in issues_low if c]
issues_medium = (c.detect() for c in checks_medium)
issues_medium = [c for c in issues_medium if c]
issues_high = [c.detect() for c in checks_high]
issues_high = [c for c in issues_high if c]
txt = "Number of informational issues: {}\n".format(green(len(issues_informational)))
txt += "Number of low issues: {}\n".format(green(len(issues_low)))
txt += "Number of medium issues: {}\n".format(yellow(len(issues_medium)))
txt += "Number of high issues: {}\n".format(red(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
result = red('Yes') if is_complex else green('No')
return "\tComplex code? {}\n".format(result)
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 += '\tNumber of functions:{}'.format(len(contract.functions))
txt += "\tIs ERC20 token: {}\n".format(contract.is_erc20())
if is_erc20:
txt += self.get_summary_erc20(contract)
self.info(txt)

@ -3,7 +3,7 @@ import os
import subprocess import subprocess
import sys import sys
from slither.detectors.abstract_detector import AbstractDetector from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.printers.abstract_printer import AbstractPrinter from slither.printers.abstract_printer import AbstractPrinter
from .solc_parsing.slitherSolc import SlitherSolc from .solc_parsing.slitherSolc import SlitherSolc
from .utils.colors import red from .utils.colors import red
@ -36,6 +36,26 @@ class Slither(SlitherSolc):
self._analyze_contracts() self._analyze_contracts()
@property
def detectors(self):
return self._detectors
@property
def detectors_high(self):
return [d for d in self.detectors if d.IMPACT == DetectorClassification.HIGH]
@property
def detectors_medium(self):
return [d for d in self.detectors if d.IMPACT == DetectorClassification.MEDIUM]
@property
def detectors_low(self):
return [d for d in self.detectors if d.IMPACT == DetectorClassification.LOW]
@property
def detectors_informational(self):
return [d for d in self.detectors if d.IMPACT == DetectorClassification.INFORMATIONAL]
def register_detector(self, detector_class): def register_detector(self, detector_class):
""" """
:param detector_class: Class inheriting from `AbstractDetector`. :param detector_class: Class inheriting from `AbstractDetector`.

@ -1,8 +1,8 @@
# Funciton computing the code complexity # Function computing the code complexity
def compute_number_edges(function): def compute_number_edges(function):
""" """
Compute the number of edges of the CFG Compute the number of edges of the CFG
Args: Args:
function (core.declarations.function.Function) function (core.declarations.function.Function)
Returns: Returns:

@ -1,3 +1,6 @@
from functools import partial
class Colors: class Colors:
RED = '\033[91m' RED = '\033[91m'
GREEN = '\033[92m' GREEN = '\033[92m'
@ -6,14 +9,13 @@ class Colors:
MAGENTA = '\033[95m' MAGENTA = '\033[95m'
END = '\033[0m' END = '\033[0m'
def green(txt):
return Colors.GREEN + txt + Colors.END
def yellow(txt):
return Colors.YELLOW + txt + Colors.END
def red(txt):
return Colors.RED + txt + Colors.END
def blue(txt):
return Colors.BLUE + txt + Colors.END
def magenta(txt):
return Colors.MAGENTA + txt + Colors.END
def colorize(color, txt):
return '{}{}{}'.format(color, txt, Colors.END)
green = partial(colorize, Colors.GREEN)
yellow = partial(colorize, Colors.YELLOW)
red = partial(colorize, Colors.RED)
blue = partial(colorize, Colors.BLUE)
magenta = partial(colorize, Colors.MAGENTA)

Loading…
Cancel
Save