def main(): args = parse_args() # Perform slither analysis on the given filename slither = Slither(args.project, **vars(args)) ret = defaultdict(list) if args.erc.upper() in ERCS: contract = slither.get_contract_from_name(args.contract_name) if not contract: err = f"Contract not found: {args.contract_name}" _log_error(err, args) return # First elem is the function, second is the event erc = ERCS[args.erc.upper()] generic_erc_checks(contract, erc[0], erc[1], ret) if args.erc.upper() in ADDITIONAL_CHECKS: ADDITIONAL_CHECKS[args.erc.upper()](contract, ret) else: err = f"Incorrect ERC selected {args.erc}" _log_error(err, args) return if args.json: output_to_json(args.json, None, {"upgradeability-check": ret})
def _run_coverage_analysis(args, slither, kspec_functions): # Collect all slither functions slither_functions = _get_slither_functions(slither) # Determine which klab specs were not resolved. slither_functions_set = set(slither_functions) kspec_functions_resolved = kspec_functions & slither_functions_set kspec_functions_unresolved = kspec_functions - kspec_functions_resolved kspec_missing = [] kspec_present = [] for slither_func_desc in sorted(slither_functions_set): slither_func = slither_functions[slither_func_desc] if slither_func_desc in kspec_functions: kspec_present.append(slither_func) else: kspec_missing.append(slither_func) logger.info("## Check for functions coverage") json_kspec_present = _generate_output(kspec_present, "[✓]", green, 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, args.json) # Handle unresolved kspecs if args.json: output.output_to_json( args.json, None, { "functions_present": json_kspec_present, "functions_missing": json_kspec_missing_functions, "variables_missing": json_kspec_missing_variables, "functions_unresolved": json_kspec_unresolved, }, )
def _log_error(err, args): if args.json: output_to_json(args.json, str(err), {"upgradeability-check": []}) logger.error(err)
def main_impl(all_detector_classes, all_printer_classes): """ :param all_detector_classes: A list of all detectors that can be included/excluded. :param all_printer_classes: A list of all printers that can be included. """ # Set logger of Slither to info, to catch warnings related to the arg parsing logger.setLevel(logging.INFO) args = parse_args(all_detector_classes, all_printer_classes) # Set colorization option set_colorization_enabled(not args.disable_color) # Define some variables for potential JSON output json_results = {} output_error = None outputting_json = args.json is not None outputting_json_stdout = args.json == '-' outputting_zip = args.zip is not None if args.zip_type not in ZIP_TYPES_ACCEPTED.keys(): logger.error(f'Zip type not accepted, it must be one of {",".join(ZIP_TYPES_ACCEPTED.keys())}') # If we are outputting JSON, capture all standard output. If we are outputting to stdout, we block typical stdout # output. if outputting_json: StandardOutputCapture.enable(outputting_json_stdout) printer_classes = choose_printers(args, all_printer_classes) detector_classes = choose_detectors(args, all_detector_classes) default_log = logging.INFO if not args.debug else logging.DEBUG for (l_name, l_level) in [('Slither', default_log), ('Contract', default_log), ('Function', default_log), ('Node', default_log), ('Parsing', default_log), ('Detectors', default_log), ('FunctionSolc', default_log), ('ExpressionParsing', default_log), ('TypeParsing', default_log), ('SSA_Conversion', default_log), ('Printers', default_log), # ('CryticCompile', default_log) ]: l = logging.getLogger(l_name) l.setLevel(l_level) console_handler = logging.StreamHandler() console_handler.setLevel(logging.INFO) console_handler.setFormatter(FormatterCryticCompile()) crytic_compile_error = logging.getLogger(('CryticCompile')) crytic_compile_error.addHandler(console_handler) crytic_compile_error.propagate = False crytic_compile_error.setLevel(logging.INFO) results_detectors = [] results_printers = [] try: filename = args.filename # Determine if we are handling ast from solc if args.solc_ast or (filename.endswith('.json') and not is_supported(filename)): globbed_filenames = glob.glob(filename, recursive=True) filenames = glob.glob(os.path.join(filename, "*.json")) if not filenames: filenames = globbed_filenames number_contracts = 0 slither_instances = [] if args.splitted: (slither_instance, results_detectors, results_printers, number_contracts) = process_from_asts(filenames, args, detector_classes, printer_classes) slither_instances.append(slither_instance) else: for filename in filenames: (slither_instance, results_detectors_tmp, results_printers_tmp, number_contracts_tmp) = process_single(filename, args, detector_classes, printer_classes) number_contracts += number_contracts_tmp results_detectors += results_detectors_tmp results_printers += results_printers_tmp slither_instances.append(slither_instance) # Rely on CryticCompile to discern the underlying type of compilations. else: (slither_instances, results_detectors, results_printers, number_contracts) = process_all(filename, args, detector_classes, printer_classes) # Determine if we are outputting JSON if outputting_json or outputting_zip: # Add our compilation information to JSON if 'compilations' in args.json_types: compilation_results = [] for slither_instance in slither_instances: compilation_results.append(generate_standard_export(slither_instance.crytic_compile)) json_results['compilations'] = compilation_results # Add our detector results to JSON if desired. if results_detectors and 'detectors' in args.json_types: json_results['detectors'] = results_detectors # Add our printer results to JSON if desired. if results_printers and 'printers' in args.json_types: json_results['printers'] = results_printers # Add our detector types to JSON if 'list-detectors' in args.json_types: detectors, _ = get_detectors_and_printers() json_results['list-detectors'] = output_detectors_json(detectors) # Add our detector types to JSON if 'list-printers' in args.json_types: _, printers = get_detectors_and_printers() json_results['list-printers'] = output_printers_json(printers) # Output our results to markdown if we wish to compile a checklist. if args.checklist: output_results_to_markdown(results_detectors) # Dont print the number of result for printers if number_contracts == 0: logger.warning(red('No contract was analyzed')) if printer_classes: logger.info('%s analyzed (%d contracts)', filename, number_contracts) else: logger.info('%s analyzed (%d contracts with %d detectors), %d result(s) found', filename, number_contracts, len(detector_classes), len(results_detectors)) logger.info(blue('Use https://crytic.io/ to get access to additional detectors and Github integration')) if args.ignore_return_value: return except SlitherException as se: output_error = str(se) traceback.print_exc() logging.error(red('Error:')) logging.error(red(output_error)) logging.error('Please report an issue to https://github.com/crytic/slither/issues') except Exception: output_error = traceback.format_exc() logging.error(traceback.print_exc()) logging.error('Error in %s' % args.filename) logging.error(output_error) # If we are outputting JSON, capture the redirected output and disable the redirect to output the final JSON. if outputting_json: if 'console' in args.json_types: json_results['console'] = { 'stdout': StandardOutputCapture.get_stdout_output(), 'stderr': StandardOutputCapture.get_stderr_output() } StandardOutputCapture.disable() output_to_json(None if outputting_json_stdout else args.json, output_error, json_results) if outputting_zip: output_to_zip(args.zip, output_error, json_results, args.zip_type) # Exit with the appropriate status code if output_error: sys.exit(-1) else: exit(results_detectors)
def main(): json_results = { 'check-initialization': defaultdict(dict), 'variable-initialization': defaultdict(dict), 'compare-function-ids': defaultdict(dict), 'compare-variables-order-implementation': defaultdict(dict), 'compare-variables-order-proxy': defaultdict(dict), 'constant_conformance': defaultdict(dict), 'proxy-present': False, 'contract_v2-present': False } args = parse_args() v1_filename = vars(args)['contract.sol'] try: v1 = Slither(v1_filename, **vars(args)) # Analyze logic contract v1_name = args.ContractName v1_contract = v1.get_contract_from_name(v1_name) if v1_contract is None: info = 'Contract {} not found in {}'.format(v1_name, v1.filename) logger.error(red(info)) if args.json: output_to_json(args.json, str(info), {"upgradeability-check": json_results}) return _checks_on_contract(v1_contract, json_results) # Analyze Proxy proxy_contract = None if args.proxy_name: if args.proxy_filename: proxy = Slither(args.proxy_filename, **vars(args)) else: proxy = v1 proxy_contract = proxy.get_contract_from_name(args.proxy_name) if proxy_contract is None: info = 'Proxy {} not found in {}'.format( args.proxy_name, proxy.filename) logger.error(red(info)) if args.json: output_to_json(args.json, str(info), {"upgradeability-check": json_results}) return json_results['proxy-present'] = True _checks_on_contract_and_proxy(v1_contract, proxy_contract, json_results) # Analyze new version if args.new_contract_name: if args.new_contract_filename: v2 = Slither(args.new_contract_filename, **vars(args)) else: v2 = v1 v2_contract = v2.get_contract_from_name(args.new_contract_name) if v2_contract is None: info = 'New logic contract {} not found in {}'.format( args.new_contract_name, v2.filename) logger.error(red(info)) if args.json: output_to_json(args.json, str(info), {"upgradeability-check": json_results}) return json_results['contract_v2-present'] = True if proxy_contract: _checks_on_contract_and_proxy(v2_contract, proxy_contract, json_results, missing_variable_check=False) _checks_on_contract_update(v1_contract, v2_contract, json_results) if args.json: output_to_json(args.json, None, {"upgradeability-check": json_results}) except SlitherException as e: logger.error(str(e)) if args.json: output_to_json(args.json, str(e), {"upgradeability-check": json_results}) return
def main_impl(all_detector_classes, all_printer_classes): """ :param all_detector_classes: A list of all detectors that can be included/excluded. :param all_printer_classes: A list of all printers that can be included. """ # Set logger of Slither to info, to catch warnings related to the arg parsing logger.setLevel(logging.INFO) args = parse_args(all_detector_classes, all_printer_classes) cp: Optional[cProfile.Profile] = None if args.perf: cp = cProfile.Profile() cp.enable() # Set colorization option set_colorization_enabled(not args.disable_color) # Define some variables for potential JSON output json_results = {} output_error = None outputting_json = args.json is not None outputting_json_stdout = args.json == "-" outputting_sarif = args.sarif is not None outputting_sarif_stdout = args.sarif == "-" outputting_zip = args.zip is not None if args.zip_type not in ZIP_TYPES_ACCEPTED.keys(): to_log = f'Zip type not accepted, it must be one of {",".join(ZIP_TYPES_ACCEPTED.keys())}' logger.error(to_log) # If we are outputting JSON, capture all standard output. If we are outputting to stdout, we block typical stdout # output. if outputting_json or output_to_sarif: StandardOutputCapture.enable(outputting_json_stdout or outputting_sarif_stdout) printer_classes = choose_printers(args, all_printer_classes) detector_classes = choose_detectors(args, all_detector_classes) default_log = logging.INFO if not args.debug else logging.DEBUG for (l_name, l_level) in [ ("Slither", default_log), ("Contract", default_log), ("Function", default_log), ("Node", default_log), ("Parsing", default_log), ("Detectors", default_log), ("FunctionSolc", default_log), ("ExpressionParsing", default_log), ("TypeParsing", default_log), ("SSA_Conversion", default_log), ("Printers", default_log), # ('CryticCompile', default_log) ]: logger_level = logging.getLogger(l_name) logger_level.setLevel(l_level) console_handler = logging.StreamHandler() console_handler.setLevel(logging.INFO) console_handler.setFormatter(FormatterCryticCompile()) crytic_compile_error = logging.getLogger(("CryticCompile")) crytic_compile_error.addHandler(console_handler) crytic_compile_error.propagate = False crytic_compile_error.setLevel(logging.INFO) results_detectors = [] results_printers = [] try: filename = args.filename # Determine if we are handling ast from solc if args.solc_ast or (filename.endswith(".json") and not is_supported(filename)): globbed_filenames = glob.glob(filename, recursive=True) filenames = glob.glob(os.path.join(filename, "*.json")) if not filenames: filenames = globbed_filenames number_contracts = 0 slither_instances = [] if args.splitted: ( slither_instance, results_detectors, results_printers, number_contracts, ) = process_from_asts(filenames, args, detector_classes, printer_classes) slither_instances.append(slither_instance) else: for filename in filenames: ( slither_instance, results_detectors_tmp, results_printers_tmp, number_contracts_tmp, ) = process_single(filename, args, detector_classes, printer_classes) number_contracts += number_contracts_tmp results_detectors += results_detectors_tmp results_printers += results_printers_tmp slither_instances.append(slither_instance) # Rely on CryticCompile to discern the underlying type of compilations. else: ( slither_instances, results_detectors, results_printers, number_contracts, ) = process_all(filename, args, detector_classes, printer_classes) # Determine if we are outputting JSON if outputting_json or outputting_zip or output_to_sarif: # Add our compilation information to JSON if "compilations" in args.json_types: compilation_results = [] for slither_instance in slither_instances: compilation_results.append( generate_standard_export( slither_instance.crytic_compile)) json_results["compilations"] = compilation_results # Add our detector results to JSON if desired. if results_detectors and "detectors" in args.json_types: json_results["detectors"] = results_detectors # Add our printer results to JSON if desired. if results_printers and "printers" in args.json_types: json_results["printers"] = results_printers # Add our detector types to JSON if "list-detectors" in args.json_types: detectors, _ = get_detectors_and_printers() json_results["list-detectors"] = output_detectors_json( detectors) # Add our detector types to JSON if "list-printers" in args.json_types: _, printers = get_detectors_and_printers() json_results["list-printers"] = output_printers_json(printers) # Output our results to markdown if we wish to compile a checklist. if args.checklist: output_results_to_markdown(results_detectors, args.checklist_limit) # Dont print the number of result for printers if number_contracts == 0: logger.warning(red("No contract was analyzed")) if printer_classes: logger.info("%s analyzed (%d contracts)", filename, number_contracts) else: logger.info( "%s analyzed (%d contracts with %d detectors), %d result(s) found", filename, number_contracts, len(detector_classes), len(results_detectors), ) if args.ignore_return_value: return except SlitherException as slither_exception: output_error = str(slither_exception) traceback.print_exc() logging.error(red("Error:")) logging.error(red(output_error)) logging.error( "Please report an issue to https://github.com/crytic/slither/issues" ) except Exception: # pylint: disable=broad-except output_error = traceback.format_exc() logging.error(traceback.print_exc()) logging.error(f"Error in {args.filename}") # pylint: disable=logging-fstring-interpolation logging.error(output_error) # If we are outputting JSON, capture the redirected output and disable the redirect to output the final JSON. if outputting_json: if "console" in args.json_types: json_results["console"] = { "stdout": StandardOutputCapture.get_stdout_output(), "stderr": StandardOutputCapture.get_stderr_output(), } StandardOutputCapture.disable() output_to_json(None if outputting_json_stdout else args.json, output_error, json_results) if outputting_sarif: StandardOutputCapture.disable() output_to_sarif(None if outputting_sarif_stdout else args.sarif, json_results, detector_classes) if outputting_zip: output_to_zip(args.zip, output_error, json_results, args.zip_type) if args.perf: cp.disable() stats = pstats.Stats(cp).sort_stats("cumtime") stats.print_stats() # Exit with the appropriate status code if output_error: sys.exit(-1) else: my_exit(results_detectors)
def main(): json_results = { "proxy-present": False, "contract_v2-present": False, "detectors": [], } args = parse_args() v1_filename = vars(args)["contract.sol"] number_detectors_run = 0 detectors = _get_checks() try: variable1 = Slither(v1_filename, **vars(args)) # Analyze logic contract v1_name = args.ContractName v1_contracts = variable1.get_contract_from_name(v1_name) if len(v1_contracts) != 1: info = f"Contract {v1_name} not found in {variable1.filename}" logger.error(red(info)) if args.json: output_to_json(args.json, str(info), json_results) return v1_contract = v1_contracts[0] detectors_results, number_detectors = _checks_on_contract( detectors, v1_contract) json_results["detectors"] += detectors_results number_detectors_run += number_detectors # Analyze Proxy proxy_contract = None if args.proxy_name: if args.proxy_filename: proxy = Slither(args.proxy_filename, **vars(args)) else: proxy = variable1 proxy_contracts = proxy.get_contract_from_name(args.proxy_name) if len(proxy_contracts) != 1: info = f"Proxy {args.proxy_name} not found in {proxy.filename}" logger.error(red(info)) if args.json: output_to_json(args.json, str(info), json_results) return proxy_contract = proxy_contracts[0] json_results["proxy-present"] = True detectors_results, number_detectors = _checks_on_contract_and_proxy( detectors, v1_contract, proxy_contract) json_results["detectors"] += detectors_results number_detectors_run += number_detectors # Analyze new version if args.new_contract_name: if args.new_contract_filename: variable2 = Slither(args.new_contract_filename, **vars(args)) else: variable2 = variable1 v2_contracts = variable2.get_contract_from_name( args.new_contract_name) if len(v2_contracts) != 1: info = ( f"New logic contract {args.new_contract_name} not found in {variable2.filename}" ) logger.error(red(info)) if args.json: output_to_json(args.json, str(info), json_results) return v2_contract = v2_contracts[0] json_results["contract_v2-present"] = True if proxy_contract: detectors_results, _ = _checks_on_contract_and_proxy( detectors, v2_contract, proxy_contract) json_results["detectors"] += detectors_results detectors_results, number_detectors = _checks_on_contract_update( detectors, v1_contract, v2_contract) json_results["detectors"] += detectors_results number_detectors_run += number_detectors # If there is a V2, we run the contract-only check on the V2 detectors_results, _ = _checks_on_contract(detectors, v2_contract) json_results["detectors"] += detectors_results number_detectors_run += number_detectors to_log = f'{len(json_results["detectors"])} findings, {number_detectors_run} detectors run' logger.info(to_log) if args.json: output_to_json(args.json, None, json_results) except SlitherException as slither_exception: logger.error(str(slither_exception)) if args.json: output_to_json(args.json, str(slither_exception), json_results) return
def main(): json_results = { 'proxy-present': False, 'contract_v2-present': False, 'detectors': [] } args = parse_args() v1_filename = vars(args)['contract.sol'] number_detectors_run = 0 detectors = _get_checks() try: v1 = Slither(v1_filename, **vars(args)) # Analyze logic contract v1_name = args.ContractName v1_contract = v1.get_contract_from_name(v1_name) if v1_contract is None: info = 'Contract {} not found in {}'.format(v1_name, v1.filename) logger.error(red(info)) if args.json: output_to_json(args.json, str(info), json_results) return detectors_results, number_detectors = _checks_on_contract( detectors, v1_contract) json_results['detectors'] += detectors_results number_detectors_run += number_detectors # Analyze Proxy proxy_contract = None if args.proxy_name: if args.proxy_filename: proxy = Slither(args.proxy_filename, **vars(args)) else: proxy = v1 proxy_contract = proxy.get_contract_from_name(args.proxy_name) if proxy_contract is None: info = 'Proxy {} not found in {}'.format( args.proxy_name, proxy.filename) logger.error(red(info)) if args.json: output_to_json(args.json, str(info), json_results) return json_results['proxy-present'] = True detectors_results, number_detectors = _checks_on_contract_and_proxy( detectors, v1_contract, proxy_contract) json_results['detectors'] += detectors_results number_detectors_run += number_detectors # Analyze new version if args.new_contract_name: if args.new_contract_filename: v2 = Slither(args.new_contract_filename, **vars(args)) else: v2 = v1 v2_contract = v2.get_contract_from_name(args.new_contract_name) if v2_contract is None: info = 'New logic contract {} not found in {}'.format( args.new_contract_name, v2.filename) logger.error(red(info)) if args.json: output_to_json(args.json, str(info), json_results) return json_results['contract_v2-present'] = True if proxy_contract: detectors_results, _ = _checks_on_contract_and_proxy( detectors, v2_contract, proxy_contract) json_results['detectors'] += detectors_results detectors_results, number_detectors = _checks_on_contract_update( detectors, v1_contract, v2_contract) json_results['detectors'] += detectors_results number_detectors_run += number_detectors # If there is a V2, we run the contract-only check on the V2 detectors_results, _ = _checks_on_contract(detectors, v2_contract) json_results['detectors'] += detectors_results number_detectors_run += number_detectors logger.info( f'{len(json_results["detectors"])} findings, {number_detectors_run} detectors run' ) if args.json: output_to_json(args.json, None, json_results) except SlitherException as e: logger.error(str(e)) if args.json: output_to_json(args.json, str(e), json_results) return