Пример #1
0
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})
Пример #2
0
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,
            },
        )
Пример #3
0
def _log_error(err, args):
    if args.json:
        output_to_json(args.json, str(err), {"upgradeability-check": []})

    logger.error(err)
Пример #4
0
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)
Пример #5
0
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
Пример #6
0
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)
Пример #7
0
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
Пример #8
0
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