|
|
|
@ -1,5 +1,8 @@ |
|
|
|
|
import re |
|
|
|
|
import logging |
|
|
|
|
|
|
|
|
|
from slither.core.declarations import Function |
|
|
|
|
from slither.core.variables.variable import Variable |
|
|
|
|
from slither.utils.colors import yellow, green, red |
|
|
|
|
from slither.utils import json_utils |
|
|
|
|
|
|
|
|
@ -46,13 +49,12 @@ def _get_all_covered_kspec_functions(target): |
|
|
|
|
def _get_slither_functions(slither): |
|
|
|
|
# Use contract == contract_declarer to avoid dupplicate |
|
|
|
|
all_functions_declared = [f for f in slither.functions if (f.contract == f.contract_declarer |
|
|
|
|
and f.is_implemented |
|
|
|
|
and not f.is_constructor |
|
|
|
|
and not f.is_empty |
|
|
|
|
and not f.is_constructor_variables)] |
|
|
|
|
# Use list(set()) because same state variable instances can be shared accross contracts |
|
|
|
|
# TODO: integrate state variables |
|
|
|
|
# all_functions_declared += list(set([s for s in slither.state_variables if s.visibility in ['public', 'external']])) |
|
|
|
|
# |
|
|
|
|
all_functions_declared += list(set([s for s in slither.state_variables if s.visibility in ['public', 'external']])) |
|
|
|
|
slither_functions = {(function.contract.name, function.full_name): function for function in all_functions_declared} |
|
|
|
|
|
|
|
|
|
return slither_functions |
|
|
|
@ -60,7 +62,7 @@ def _get_slither_functions(slither): |
|
|
|
|
def _generate_output(kspec, message, color, generate_json): |
|
|
|
|
info = "" |
|
|
|
|
for function in kspec: |
|
|
|
|
info += f"{message} {function.contract.name}.{function.full_name}" |
|
|
|
|
info += f"{message} {function.contract.name}.{function.full_name}\n" |
|
|
|
|
if info: |
|
|
|
|
logger.info(color(info)) |
|
|
|
|
|
|
|
|
@ -74,7 +76,7 @@ def _generate_output(kspec, message, color, generate_json): |
|
|
|
|
def _generate_output_unresolved(kspec, message, color, generate_json): |
|
|
|
|
info = "" |
|
|
|
|
for contract, function in kspec: |
|
|
|
|
info += f"{message} {contract}.{function}" |
|
|
|
|
info += f"{message} {contract}.{function}\n" |
|
|
|
|
if info: |
|
|
|
|
logger.info(color(info)) |
|
|
|
|
|
|
|
|
@ -84,7 +86,6 @@ def _generate_output_unresolved(kspec, message, color, generate_json): |
|
|
|
|
return None |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _run_coverage_analysis(args, slither, kspec_functions): |
|
|
|
|
# Collect all slither functions |
|
|
|
|
slither_functions = _get_slither_functions(slither) |
|
|
|
@ -108,7 +109,14 @@ def _run_coverage_analysis(args, slither, kspec_functions): |
|
|
|
|
|
|
|
|
|
logger.info('## Check for functions coverage') |
|
|
|
|
json_kspec_present = _generate_output(kspec_present, "[✓]", green, args.json) |
|
|
|
|
json_kspec_missing = _generate_output(kspec_missing, "[ ] (Missing)", red, args.json) |
|
|
|
|
json_kspec_missing_functions = _generate_output([f for f in kspec_missing if isinstance(f, Function)], |
|
|
|
|
"[ ] (Missing function)", |
|
|
|
|
red, |
|
|
|
|
args.json) |
|
|
|
|
json_kspec_missing_variables = _generate_output([f for f in kspec_missing if isinstance(f, Variable)], |
|
|
|
|
"[ ] (Missing variable)", |
|
|
|
|
yellow, |
|
|
|
|
args.json) |
|
|
|
|
json_kspec_unresolved = _generate_output_unresolved(kspec_functions_unresolved, |
|
|
|
|
"[ ] (Unresolved)", |
|
|
|
|
yellow, |
|
|
|
@ -117,14 +125,21 @@ def _run_coverage_analysis(args, slither, kspec_functions): |
|
|
|
|
# Handle unresolved kspecs |
|
|
|
|
if args.json: |
|
|
|
|
json_utils.output_json(args.json, None, { |
|
|
|
|
"kspec_present": json_kspec_present, |
|
|
|
|
"kspec_missing": json_kspec_missing, |
|
|
|
|
"kspec_unresolved": json_kspec_unresolved |
|
|
|
|
"functions_present": json_kspec_present, |
|
|
|
|
"functions_missing": json_kspec_missing_functions, |
|
|
|
|
"variables_missing": json_kspec_missing_variables, |
|
|
|
|
"functions_unresolved": json_kspec_unresolved |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
def run_analysis(args, slither, kspec): |
|
|
|
|
# Get all of our kspec'd functions (tuple(contract_name, function_name)). |
|
|
|
|
kspec_functions = _get_all_covered_kspec_functions(kspec) |
|
|
|
|
if ',' in kspec: |
|
|
|
|
kspecs = kspec.split(',') |
|
|
|
|
kspec_functions = set() |
|
|
|
|
for kspec in kspecs: |
|
|
|
|
kspec_functions |= _get_all_covered_kspec_functions(kspec) |
|
|
|
|
else: |
|
|
|
|
kspec_functions = _get_all_covered_kspec_functions(kspec) |
|
|
|
|
|
|
|
|
|
# Run coverage analysis |
|
|
|
|
_run_coverage_analysis(args, slither, kspec_functions) |
|
|
|
|