diff --git a/bandit/cli/main.py b/bandit/cli/main.py index 119380b28..f38dd9122 100644 --- a/bandit/cli/main.py +++ b/bandit/cli/main.py @@ -340,7 +340,8 @@ def main(): help="comma-separated list of paths (glob patterns " "supported) to exclude from scan " "(note that these are in addition to the excluded " - "paths provided in the config file) (default: " + "paths provided in the config file and any files " + "matching patterns defined in .gitignore) (default: " + ",".join(constants.EXCLUDE) + ")", ) diff --git a/bandit/core/manager.py b/bandit/core/manager.py index 57e0e8570..82097423d 100644 --- a/bandit/core/manager.py +++ b/bandit/core/manager.py @@ -13,6 +13,7 @@ import tokenize import traceback +import ignorelib from rich import progress from bandit.core import constants as b_constants @@ -226,10 +227,11 @@ def discover_files(self, targets, recursive=False, excluded_paths=""): if os.path.isdir(fname): if recursive: new_files, newly_excluded = _get_files_from_dir( - fname, + _build_gitignore_mgr(fname), included_globs=included_globs, excluded_path_strings=excluded_path_globs, ) + files_list.update(new_files) excluded_files.update(newly_excluded) else: @@ -238,7 +240,6 @@ def discover_files(self, targets, recursive=False, excluded_paths=""): "scan contents", fname, ) - else: # if the user explicitly mentions a file on command line, # we'll scan it, regardless of whether it's in the included @@ -365,8 +366,17 @@ def _execute_ast_visitor(self, fname, fdata, data, nosec_lines): return score +def _build_gitignore_mgr(path): + return ignorelib.IgnoreFilterManager.build( + path, + global_ignore_file_paths=[], + global_patterns=[], + ignore_file_name=".gitignore", + ) + + def _get_files_from_dir( - files_dir, included_globs=None, excluded_path_strings=None + ignore_mgr, included_globs=None, excluded_path_strings=None ): if not included_globs: included_globs = ["*.py"] @@ -376,7 +386,7 @@ def _get_files_from_dir( files_list = set() excluded_files = set() - for root, _, files in os.walk(files_dir): + for root, _, files in ignore_mgr.walk(): for filename in files: path = os.path.join(root, filename) if _is_file_included(path, included_globs, excluded_path_strings): diff --git a/doc/source/man/bandit.rst b/doc/source/man/bandit.rst index 46125e613..dffa63f5f 100644 --- a/doc/source/man/bandit.rst +++ b/doc/source/man/bandit.rst @@ -62,7 +62,8 @@ OPTIONS comma-separated list of paths (glob patterns supported) to exclude from scan (note that these are in addition to the excluded paths provided in the - config file) (default: + config file and any files matching patterns defined in + .gitignore) (default: .svn,CVS,.bzr,.hg,.git,__pycache__,.tox,.eggs,*.egg) -b BASELINE, --baseline BASELINE path of a baseline report to compare against (only diff --git a/requirements.txt b/requirements.txt index 289782022..77348fba0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,4 @@ PyYAML>=5.3.1 # MIT stevedore>=1.20.0 # Apache-2.0 colorama>=0.3.9;platform_system=="Windows" # BSD License (3 clause) rich # MIT +ignorelib # Apache-2.0 diff --git a/tests/unit/core/test_manager.py b/tests/unit/core/test_manager.py index df815f588..544c3c030 100644 --- a/tests/unit/core/test_manager.py +++ b/tests/unit/core/test_manager.py @@ -113,15 +113,17 @@ def test_is_file_included(self): self.assertFalse(e) self.assertTrue(f) - @mock.patch("os.walk") - def test_get_files_from_dir(self, os_walk): - os_walk.return_value = [ + def test_get_files_from_dir(self): + ignore_walk = mock.Mock() + ignore_walk.walk.return_value = [ ("/", ("a"), ()), ("/a", (), ("a.py", "b.py", "c.ww")), ] inc, exc = manager._get_files_from_dir( - files_dir="", included_globs=["*.py"], excluded_path_strings=None + ignore_mgr=ignore_walk, + included_globs=["*.py"], + excluded_path_strings=None, ) self.assertEqual({"/a/c.ww"}, exc)