diff --git a/slither/__main__.py b/slither/__main__.py index 3cd914a87..ee1428967 100644 --- a/slither/__main__.py +++ b/slither/__main__.py @@ -268,9 +268,10 @@ def choose_printers( ################################################################################### -def parse_filter_paths(args: argparse.Namespace) -> List[str]: - if args.filter_paths: - return args.filter_paths.split(",") +def parse_filter_paths(args: argparse.Namespace, filter_path: bool) -> List[str]: + paths = args.filter_paths if filter_path else args.include_paths + if paths: + return paths.split(",") return [] @@ -307,6 +308,7 @@ def parse_args( "Checklist (consider using https://github.com/crytic/slither-action)" ) group_misc = parser.add_argument_group("Additional options") + group_filters = parser.add_mutually_exclusive_group() group_detector.add_argument( "--detect", @@ -518,14 +520,6 @@ def parse_args( default=defaults_flag_in_config["disable_color"], ) - group_misc.add_argument( - "--filter-paths", - help="Regex filter to exclude detector results matching file path e.g. (mocks/|test/)", - action="store", - dest="filter_paths", - default=defaults_flag_in_config["filter_paths"], - ) - group_misc.add_argument( "--triage-mode", help="Run triage mode (save results in triage database)", @@ -579,6 +573,22 @@ def parse_args( default=defaults_flag_in_config["no_fail"], ) + group_filters.add_argument( + "--filter-paths", + help="Regex filter to exclude detector results matching file path e.g. (mocks/|test/)", + action="store", + dest="filter_paths", + default=defaults_flag_in_config["filter_paths"], + ) + + group_filters.add_argument( + "--include-paths", + help="Regex filter to include detector results matching file path e.g. (src/|contracts/). Opposite of --filter-paths", + action="store", + dest="include_paths", + default=defaults_flag_in_config["include_paths"], + ) + codex.init_parser(parser) # debugger command @@ -631,7 +641,8 @@ def parse_args( args = parser.parse_args() read_config_file(args) - args.filter_paths = parse_filter_paths(args) + args.filter_paths = parse_filter_paths(args, True) + args.include_paths = parse_filter_paths(args, False) # Verify our json-type output is valid args.json_types = set(args.json_types.split(",")) # type:ignore diff --git a/slither/core/slither_core.py b/slither/core/slither_core.py index b1bb7c66b..76220f5ba 100644 --- a/slither/core/slither_core.py +++ b/slither/core/slither_core.py @@ -62,6 +62,7 @@ def __init__(self) -> None: # Multiple time the same result, so we remove duplicates self._currently_seen_resuts: Set[str] = set() self._paths_to_filter: Set[str] = set() + self._paths_to_include: Set[str] = set() self._crytic_compile: Optional[CryticCompile] = None @@ -411,25 +412,29 @@ def valid_result(self, r: Dict) -> bool: if "source_mapping" in elem ] - # Use POSIX-style paths so that filter_paths works across different + # Use POSIX-style paths so that filter_paths|include_paths works across different # OSes. Convert to a list so elements don't get consumed and are lost # while evaluating the first pattern source_mapping_elements = list( map(lambda x: pathlib.Path(x).resolve().as_posix() if x else x, source_mapping_elements) ) - matching = False + (matching, paths, msg_err) = ( + (True, self._paths_to_include, "--include-paths") + if self._paths_to_include + else (False, self._paths_to_filter, "--filter-paths") + ) - for path in self._paths_to_filter: + for path in paths: try: if any( bool(re.search(_relative_path_format(path), src_mapping)) for src_mapping in source_mapping_elements ): - matching = True + matching = not matching break except re.error: logger.error( - f"Incorrect regular expression for --filter-paths {path}." + f"Incorrect regular expression for {msg_err} {path}." "\nSlither supports the Python re format" ": https://docs.python.org/3/library/re.html" ) @@ -500,6 +505,13 @@ def add_path_to_filter(self, path: str): """ self._paths_to_filter.add(path) + def add_path_to_include(self, path: str): + """ + Add path to include + Path are used through direct comparison (no regex) + """ + self._paths_to_include.add(path) + # endregion ################################################################################### ################################################################################### diff --git a/slither/slither.py b/slither/slither.py index a3cc64404..4259b74b7 100644 --- a/slither/slither.py +++ b/slither/slither.py @@ -132,6 +132,10 @@ def __init__(self, target: Union[str, CryticCompile], **kwargs) -> None: for p in filter_paths: self.add_path_to_filter(p) + include_paths = kwargs.get("include_paths", []) + for p in include_paths: + self.add_path_to_include(p) + self._exclude_dependencies = kwargs.get("exclude_dependencies", False) triage_mode = kwargs.get("triage_mode", False) diff --git a/slither/utils/command_line.py b/slither/utils/command_line.py index 2432a2116..f03ced834 100644 --- a/slither/utils/command_line.py +++ b/slither/utils/command_line.py @@ -60,6 +60,7 @@ class FailOnLevel(enum.Enum): "json-types": ",".join(DEFAULT_JSON_OUTPUT_TYPES), "disable_color": False, "filter_paths": None, + "include_paths": None, "generate_patches": False, # debug command "skip_assembly": False,