|
|
@ -13,33 +13,53 @@ class PrinterHumanSummary(AbstractPrinter): |
|
|
|
HELP = 'Print a human readable summary of the contracts' |
|
|
|
HELP = 'Print a human readable summary of the contracts' |
|
|
|
|
|
|
|
|
|
|
|
@staticmethod |
|
|
|
@staticmethod |
|
|
|
def get_summary_erc20(contract): |
|
|
|
def _get_summary_erc20(contract): |
|
|
|
txt = '' |
|
|
|
|
|
|
|
functions_name = [f.name for f in contract.functions] |
|
|
|
functions_name = [f.name for f in contract.functions] |
|
|
|
state_variables = [v.name for v in contract.state_variables] |
|
|
|
state_variables = [v.name for v in contract.state_variables] |
|
|
|
|
|
|
|
|
|
|
|
if 'pause' in functions_name: |
|
|
|
pause = '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 'mint' in functions_name: |
|
|
|
if not 'mintingFinished' in state_variables: |
|
|
|
if not 'mintingFinished' in state_variables: |
|
|
|
txt += "\t\t Minting restriction? : {}\n".format(red('None')) |
|
|
|
mint_limited = False |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
mint_limited = True |
|
|
|
else: |
|
|
|
else: |
|
|
|
txt += "\t\t Minting restriction? : {}\n".format(yellow('Yes')) |
|
|
|
mint = None # no minting |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
race_condition_mitigated = 'increaseApproval' in functions_name or\ |
|
|
|
|
|
|
|
'safeIncreaseAllowance' in functions_name |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return pause, mint_limited, race_condition_mitigated |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_summary_erc20(self, contract): |
|
|
|
|
|
|
|
txt = '' |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pause, mint_limited, race_condition_mitigated = self._get_summary_erc20(contract) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if pause: |
|
|
|
|
|
|
|
txt += "\t\t Can be paused? : {}\n".format(yellow('Yes')) |
|
|
|
else: |
|
|
|
else: |
|
|
|
|
|
|
|
txt += "\t\t Can be paused? : {}\n".format(green('No')) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if mint_limited is None: |
|
|
|
txt += "\t\t Minting restriction? : {}\n".format(green('No Minting')) |
|
|
|
txt += "\t\t Minting restriction? : {}\n".format(green('No Minting')) |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
if mint_limited: |
|
|
|
|
|
|
|
txt += "\t\t Minting restriction? : {}\n".format(red('Yes')) |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
txt += "\t\t Minting restriction? : {}\n".format(yellow('No')) |
|
|
|
|
|
|
|
|
|
|
|
if 'increaseApproval' in functions_name or 'safeIncreaseAllowance' in functions_name: |
|
|
|
if race_condition_mitigated: |
|
|
|
txt += "\t\t ERC20 race condition mitigation: {}\n".format(green('Yes')) |
|
|
|
txt += "\t\t ERC20 race condition mitigation: {}\n".format(green('Yes')) |
|
|
|
else: |
|
|
|
else: |
|
|
|
txt += "\t\t ERC20 race condition mitigation: {}\n".format(red('No')) |
|
|
|
txt += "\t\t ERC20 race condition mitigation: {}\n".format(red('No')) |
|
|
|
|
|
|
|
|
|
|
|
return txt |
|
|
|
return txt |
|
|
|
|
|
|
|
|
|
|
|
def get_detectors_result(self): |
|
|
|
def _get_detectors_result(self): |
|
|
|
|
|
|
|
|
|
|
|
# disable detectors logger |
|
|
|
# disable detectors logger |
|
|
|
logger = logging.getLogger('Detectors') |
|
|
|
logger = logging.getLogger('Detectors') |
|
|
|
logger.setLevel(logging.ERROR) |
|
|
|
logger.setLevel(logging.ERROR) |
|
|
@ -57,16 +77,28 @@ class PrinterHumanSummary(AbstractPrinter): |
|
|
|
issues_medium = [c for c in issues_medium if c] |
|
|
|
issues_medium = [c for c in issues_medium if c] |
|
|
|
issues_high = [c.detect() for c in checks_high] |
|
|
|
issues_high = [c.detect() for c in checks_high] |
|
|
|
issues_high = [c for c in issues_high if c] |
|
|
|
issues_high = [c for c in issues_high if c] |
|
|
|
|
|
|
|
return (len(issues_informational), |
|
|
|
|
|
|
|
len(issues_low), |
|
|
|
|
|
|
|
len(issues_medium), |
|
|
|
|
|
|
|
len(issues_high)) |
|
|
|
|
|
|
|
|
|
|
|
txt = "Number of informational issues: {}\n".format(green(len(issues_informational))) |
|
|
|
def get_detectors_result(self): |
|
|
|
txt += "Number of low issues: {}\n".format(green(len(issues_low))) |
|
|
|
issues_informational, issues_low, issues_medium, issues_high = self._get_detectors_result() |
|
|
|
txt += "Number of medium issues: {}\n".format(yellow(len(issues_medium))) |
|
|
|
txt = "Number of informational issues: {}\n".format(green(issues_informational)) |
|
|
|
txt += "Number of high issues: {}\n".format(red(len(issues_high))) |
|
|
|
txt += "Number of low issues: {}\n".format(green(issues_low)) |
|
|
|
|
|
|
|
txt += "Number of medium issues: {}\n".format(yellow(issues_medium)) |
|
|
|
|
|
|
|
txt += "Number of high issues: {}\n".format(red(issues_high)) |
|
|
|
|
|
|
|
|
|
|
|
return txt |
|
|
|
return txt |
|
|
|
|
|
|
|
|
|
|
|
@staticmethod |
|
|
|
@staticmethod |
|
|
|
def is_complex_code(contract): |
|
|
|
def _is_complex_code(contract): |
|
|
|
|
|
|
|
for f in contract.functions: |
|
|
|
|
|
|
|
if compute_cyclomatic_complexity(f) > 7: |
|
|
|
|
|
|
|
return True |
|
|
|
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def is_complex_code(self, contract): |
|
|
|
""" |
|
|
|
""" |
|
|
|
Check if the code is complex |
|
|
|
Check if the code is complex |
|
|
|
Heuristic, the code is complex if: |
|
|
|
Heuristic, the code is complex if: |
|
|
@ -74,16 +106,17 @@ class PrinterHumanSummary(AbstractPrinter): |
|
|
|
Args: |
|
|
|
Args: |
|
|
|
contract |
|
|
|
contract |
|
|
|
""" |
|
|
|
""" |
|
|
|
is_complex = False |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for f in contract.functions: |
|
|
|
is_complex = self._is_complex_code(contract) |
|
|
|
if compute_cyclomatic_complexity(f) > 7: |
|
|
|
|
|
|
|
is_complex = True |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
result = red('Yes') if is_complex else green('No') |
|
|
|
result = red('Yes') if is_complex else green('No') |
|
|
|
|
|
|
|
|
|
|
|
return "\tComplex code? {}\n".format(result) |
|
|
|
return "\tComplex code? {}\n".format(result) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod |
|
|
|
|
|
|
|
def _number_functions(contract): |
|
|
|
|
|
|
|
return len(contract.functions) |
|
|
|
|
|
|
|
|
|
|
|
def output(self, _filename): |
|
|
|
def output(self, _filename): |
|
|
|
""" |
|
|
|
""" |
|
|
|
_filename is not used |
|
|
|
_filename is not used |
|
|
@ -97,7 +130,7 @@ class PrinterHumanSummary(AbstractPrinter): |
|
|
|
txt += "\nContract {}\n".format(contract.name) |
|
|
|
txt += "\nContract {}\n".format(contract.name) |
|
|
|
txt += self.is_complex_code(contract) |
|
|
|
txt += self.is_complex_code(contract) |
|
|
|
is_erc20 = contract.is_erc20() |
|
|
|
is_erc20 = contract.is_erc20() |
|
|
|
txt += '\tNumber of functions:{}'.format(len(contract.functions)) |
|
|
|
txt += '\tNumber of functions:{}'.format(self._number_functions(contract)) |
|
|
|
txt += "\tIs ERC20 token: {}\n".format(contract.is_erc20()) |
|
|
|
txt += "\tIs ERC20 token: {}\n".format(contract.is_erc20()) |
|
|
|
if is_erc20: |
|
|
|
if is_erc20: |
|
|
|
txt += self.get_summary_erc20(contract) |
|
|
|
txt += self.get_summary_erc20(contract) |
|
|
|