diff --git a/slither/__main__.py b/slither/__main__.py index d8fce1ce55..9d611532ea 100644 --- a/slither/__main__.py +++ b/slither/__main__.py @@ -555,6 +555,13 @@ def parse_args( default=False, ) + group_misc.add_argument( + "--no-fail", + help="Do not fail in case of parsing (echidna mode only)", + action="store_true", + default=defaults_flag_in_config["no_fail"], + ) + codex.init_parser(parser) # debugger command diff --git a/slither/core/slither_core.py b/slither/core/slither_core.py index 6916990671..66b8fc4308 100644 --- a/slither/core/slither_core.py +++ b/slither/core/slither_core.py @@ -92,6 +92,10 @@ def __init__(self): # But we allow to alter this (ex: file.sol:1) for vscode integration self.line_prefix: str = "#" + # Use by the echidna printer + # If true, partial analysis is allowed + self.no_fail = False + @property def compilation_units(self) -> List[SlitherCompilationUnit]: return list(self._compilation_units) diff --git a/slither/printers/guidance/echidna.py b/slither/printers/guidance/echidna.py index 45b3ab3327..95d113a84b 100644 --- a/slither/printers/guidance/echidna.py +++ b/slither/printers/guidance/echidna.py @@ -329,27 +329,32 @@ def _call_a_parameter(slither: SlitherCore) -> Dict[str, List[Dict]]: ret: Dict[str, List[Dict]] = defaultdict(list) for contract in slither.contracts: # pylint: disable=too-many-nested-blocks for function in contract.functions_entry_points: - for ir in function.all_slithir_operations(): - if isinstance(ir, HighLevelCall): - for idx, parameter in enumerate(function.parameters): - if is_dependent(ir.destination, parameter, function): - ret[contract.name].append( - { - "function": _get_name(function), - "parameter_idx": idx, - "signature": _get_name(ir.function), - } - ) - if isinstance(ir, LowLevelCall): - for idx, parameter in enumerate(function.parameters): - if is_dependent(ir.destination, parameter, function): - ret[contract.name].append( - { - "function": _get_name(function), - "parameter_idx": idx, - "signature": None, - } - ) + try: + for ir in function.all_slithir_operations(): + if isinstance(ir, HighLevelCall): + for idx, parameter in enumerate(function.parameters): + if is_dependent(ir.destination, parameter, function): + ret[contract.name].append( + { + "function": _get_name(function), + "parameter_idx": idx, + "signature": _get_name(ir.function), + } + ) + if isinstance(ir, LowLevelCall): + for idx, parameter in enumerate(function.parameters): + if is_dependent(ir.destination, parameter, function): + ret[contract.name].append( + { + "function": _get_name(function), + "parameter_idx": idx, + "signature": None, + } + ) + except Exception as e: + if slither.no_fail: + continue + raise e return ret diff --git a/slither/slither.py b/slither/slither.py index 81e920d013..45d99906fd 100644 --- a/slither/slither.py +++ b/slither/slither.py @@ -91,6 +91,8 @@ def __init__(self, target: Union[str, CryticCompile], **kwargs): self.codex_max_tokens = kwargs.get("codex_max_tokens", 300) self.codex_log = kwargs.get("codex_log", False) + self.no_fail = kwargs.get("no_fail", False) + self._parsers: List[SlitherCompilationUnitSolc] = [] try: if isinstance(target, CryticCompile): @@ -128,41 +130,27 @@ def __init__(self, target: Union[str, CryticCompile], **kwargs): triage_mode = kwargs.get("triage_mode", False) self._triage_mode = triage_mode + self._init_parsing_and_analyses(kwargs.get("skip_analyze", False)) + + def _init_parsing_and_analyses(self, skip_analyze: bool) -> None: for parser in self._parsers: - parser.parse_contracts() + try: + parser.parse_contracts() + except Exception as e: + if self.no_fail: + continue + raise e # skip_analyze is only used for testing - if not kwargs.get("skip_analyze", False): + if not skip_analyze: for parser in self._parsers: - parser.analyze_contracts() - - # def _init_from_raw_json(self, filename): - # if not os.path.isfile(filename): - # raise SlitherError( - # "{} does not exist (are you in the correct directory?)".format(filename) - # ) - # assert filename.endswith("json") - # with open(filename, encoding="utf8") as astFile: - # stdout = astFile.read() - # if not stdout: - # to_log = f"Empty AST file: {filename}" - # raise SlitherError(to_log) - # contracts_json = stdout.split("\n=") - # - # self._parser = SlitherCompilationUnitSolc(filename, self) - # - # for c in contracts_json: - # self._parser.parse_top_level_from_json(c) - - # def _init_from_list(self, contract): - # self._parser = SlitherCompilationUnitSolc("", self) - # for c in contract: - # if "absolutePath" in c: - # path = c["absolutePath"] - # else: - # path = c["attributes"]["absolutePath"] - # self._parser.parse_top_level_from_loaded_json(c, path) + try: + parser.analyze_contracts() + except Exception as e: + if self.no_fail: + continue + raise e @property def detectors(self): diff --git a/slither/utils/command_line.py b/slither/utils/command_line.py index 71305c56e2..174c2a4b62 100644 --- a/slither/utils/command_line.py +++ b/slither/utils/command_line.py @@ -60,6 +60,7 @@ "zip": None, "zip_type": "lzma", "show_ignored_findings": False, + "no_fail": False, **DEFAULTS_FLAG_IN_CONFIG_CRYTIC_COMPILE, }