From b249baf9c665f113741354bf2e583b731223a45d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Str=C3=B6mich?= Date: Sat, 12 Jun 2021 19:42:13 +0200 Subject: [PATCH 1/6] #640 - exit zero severity --- bandit/cli/main.py | 25 +++++++++++++++++++++-- bandit/core/manager.py | 18 +++++++++++++++++ doc/source/man/bandit.rst | 2 ++ tests/unit/cli/test_main.py | 32 ++++++++++++++++++++++++++++++ tests/unit/core/test_manager.py | 35 +++++++++++++++++++++++++++++++++ 5 files changed, 110 insertions(+), 2 deletions(-) diff --git a/bandit/cli/main.py b/bandit/cli/main.py index 66f6b4d0e..cf7e9a021 100644 --- a/bandit/cli/main.py +++ b/bandit/cli/main.py @@ -264,6 +264,11 @@ def main(): parser.add_argument('--exit-zero', action='store_true', dest='exit_zero', default=False, help='exit with 0, ' 'even with results found') + parser.add_argument( + '--exit-zero-severity', dest='exit_zero_severity_string', action='store', default=None, + help='control which severity makes bandit to exit with zero status code. ' + 'Lower severities to the specified one are included implicitly' + ) python_ver = sys.version.replace('\n', '') parser.add_argument( '--version', action='version', @@ -343,6 +348,17 @@ def main(): args.confidence = 4 # Other strings will be blocked by argparse + if args.exit_zero_severity_string is not None: + if args.exit_zero_severity_string == "all": + args.exit_zero_severity = 1 + elif args.exit_zero_severity_string == "low": + args.exit_zero_severity = 2 + elif args.exit_zero_severity_string == "medium": + args.exit_zero_severity = 3 + elif args.exit_zero_severity_string == "high": + args.exit_zero_severity = 4 + # Other strings will be blocked by argparse + try: b_conf = b_config.BanditConfig(config_file=args.config_file) except utils.ConfigError as e: @@ -520,8 +536,13 @@ def main(): args.output_format, args.msg_template) - if (b_mgr.results_count(sev_filter=sev_level, conf_filter=conf_level) > 0 - and not args.exit_zero): + if args.exit_zero: + sys.exit(0) + + if "exit_zero_severity" in args and not b_mgr.above_threshold_results(args.exit_zero_severity): + sys.exit(0) + + if b_mgr.results_count(sev_filter=sev_level, conf_filter=conf_level) > 0: sys.exit(1) else: sys.exit(0) diff --git a/bandit/core/manager.py b/bandit/core/manager.py index 466670c8a..76857ebcd 100644 --- a/bandit/core/manager.py +++ b/bandit/core/manager.py @@ -320,6 +320,24 @@ def _execute_ast_visitor(self, fname, data, nosec_lines): self.results.extend(res.tester.results) return score + def above_threshold_results(self, exit_zero_severity): + """ + this method takes args.exit_zero_severity and checkes the count of results of all severities above the defined exit zero severity. + if any of the above severities reports > 0 results this method returns True else it returns False + """ + items_in_rankings = len(b_constants.RANKING) + # this is the minimal level we shouldn't exit with 0 + non_exit_zero_severity = exit_zero_severity + 1 + + while non_exit_zero_severity <= items_in_rankings: + some_var = b_constants.RANKING[non_exit_zero_severity - 1] + results_count = self.results_count(sev_filter=some_var) + non_exit_zero_severity += 1 + + if results_count > 0: + return True + + return False def _get_files_from_dir(files_dir, included_globs=None, excluded_path_strings=None): diff --git a/doc/source/man/bandit.rst b/doc/source/man/bandit.rst index 46125e613..037b86346 100644 --- a/doc/source/man/bandit.rst +++ b/doc/source/man/bandit.rst @@ -70,6 +70,8 @@ OPTIONS --ini INI_PATH path to a .bandit file that supplies command line arguments --exit-zero exit with 0, even with results found + --exit-zero-severity control which severity makes bandit to exit with zero status code. + Lower severities to the specified one are included implicitly --version show program's version number and exit CUSTOM FORMATTING diff --git a/tests/unit/cli/test_main.py b/tests/unit/cli/test_main.py index 815e3c118..ef1031857 100644 --- a/tests/unit/cli/test_main.py +++ b/tests/unit/cli/test_main.py @@ -291,3 +291,35 @@ def test_main_exit_with_results_and_with_exit_zero_flag(self): mock_mgr_results_ct.return_value = 1 self.assertRaisesRegex(SystemExit, '0', bandit.main) + + @mock.patch('sys.argv', ['bandit', '-c', 'bandit.yaml', 'test', '-o', + 'output', '--exit-zero-severity', 'low']) + def test_main_exit_with_results_and_with_exit_zero_severity_flag_set_to_some_value_returning_true(self): + # Test that bandit exits with 0 on results and zero flag + temp_directory = self.useFixture(fixtures.TempDir()).path + os.chdir(temp_directory) + with open('bandit.yaml', 'wt') as fd: + fd.write(bandit_config_content) + with mock.patch('bandit.core.manager.BanditManager.results_count' + ) as mock_mgr_results_ct: + mock_mgr_results_ct.return_value = 2 + with mock.patch('bandit.core.manager.BanditManager.above_threshold_results' + ) as mock_mgr_above_threshold_results_ct: + mock_mgr_above_threshold_results_ct.return_value = True + + + self.assertRaisesRegex(SystemExit, '1', bandit.main) + + @mock.patch('sys.argv', ['bandit', '-c', 'bandit.yaml', 'test', '-o', + 'output', '--exit-zero-severity', 'medium']) + def test_main_exit_with_results_and_with_exit_zero_severity_flag_set_to_some_value_returning_false(self): + # Test that bandit exits with 0 on results and zero flag + temp_directory = self.useFixture(fixtures.TempDir()).path + os.chdir(temp_directory) + with open('bandit.yaml', 'wt') as fd: + fd.write(bandit_config_content) + with mock.patch('bandit.core.manager.BanditManager.above_threshold_results' + ) as mock_mgr_above_threshold_results_ct: + mock_mgr_above_threshold_results_ct.return_value = False + + self.assertRaisesRegex(SystemExit, '0', bandit.main) diff --git a/tests/unit/core/test_manager.py b/tests/unit/core/test_manager.py index 0ee928e0e..96563f716 100644 --- a/tests/unit/core/test_manager.py +++ b/tests/unit/core/test_manager.py @@ -333,3 +333,38 @@ def test_find_candidate_matches(self): {issue_a: [issue_a, issue_b], issue_b: [issue_a, issue_b]}, manager._find_candidate_matches([issue_a, issue_b], [issue_a, issue_b, issue_c])) + + def test_above_threshold_medium_severity_results_true(self): + levels = [constants.LOW, constants.MEDIUM] + self.manager.results = ( + [issue.Issue(severity=level, confidence=level) + for level in levels]) + self.assertTrue(self.manager.above_threshold_results(2)) + + def test_above_threshold_high_severity_results_true(self): + levels = [constants.LOW, constants.HIGH] + self.manager.results = ( + [issue.Issue(severity=level, confidence=level) + for level in levels]) + self.assertTrue(self.manager.above_threshold_results(2)) + + def test_above_threshold_low_severity_results_false(self): + levels = [constants.LOW] + self.manager.results = ( + [issue.Issue(severity=level, confidence=level) + for level in levels]) + self.assertFalse(self.manager.above_threshold_results(2)) + + def test_above_threshold_medium_severity_results_false(self): + levels = [constants.MEDIUM] + self.manager.results = ( + [issue.Issue(severity=level, confidence=level) + for level in levels]) + self.assertFalse(self.manager.above_threshold_results(3)) + + def test_above_threshold_high_severity_results_false(self): + levels = [constants.HIGH] + self.manager.results = ( + [issue.Issue(severity=level, confidence=level) + for level in levels]) + self.assertFalse(self.manager.above_threshold_results(4)) From c806f5df8a8584ae5bcbf52f031fdd0479c516d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Str=C3=B6mich?= Date: Sat, 12 Jun 2021 20:37:57 +0200 Subject: [PATCH 2/6] pep8 check --- bandit/cli/main.py | 11 +++++++---- bandit/core/manager.py | 17 +++++++++++++---- tests/unit/cli/test_main.py | 15 ++++++++------- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/bandit/cli/main.py b/bandit/cli/main.py index cf7e9a021..d3a3bca1b 100644 --- a/bandit/cli/main.py +++ b/bandit/cli/main.py @@ -265,9 +265,11 @@ def main(): default=False, help='exit with 0, ' 'even with results found') parser.add_argument( - '--exit-zero-severity', dest='exit_zero_severity_string', action='store', default=None, - help='control which severity makes bandit to exit with zero status code. ' - 'Lower severities to the specified one are included implicitly' + '--exit-zero-severity', dest='exit_zero_severity_string', + action='store', default=None, + help='control which severity makes bandit to exit with zero ' + 'status code. Lower severities to the specified one are ' + 'included implicitly' ) python_ver = sys.version.replace('\n', '') parser.add_argument( @@ -539,7 +541,8 @@ def main(): if args.exit_zero: sys.exit(0) - if "exit_zero_severity" in args and not b_mgr.above_threshold_results(args.exit_zero_severity): + if ("exit_zero_severity" in args + and not b_mgr.above_threshold_results(args.exit_zero_severity)): sys.exit(0) if b_mgr.results_count(sev_filter=sev_level, conf_filter=conf_level) > 0: diff --git a/bandit/core/manager.py b/bandit/core/manager.py index 76857ebcd..75cbc0d74 100644 --- a/bandit/core/manager.py +++ b/bandit/core/manager.py @@ -321,10 +321,18 @@ def _execute_ast_visitor(self, fname, data, nosec_lines): return score def above_threshold_results(self, exit_zero_severity): - """ - this method takes args.exit_zero_severity and checkes the count of results of all severities above the defined exit zero severity. - if any of the above severities reports > 0 results this method returns True else it returns False - """ + '''Check the above threshold results + + this method takes args.exit_zero_severity and checkes the count + of results of all severities above the defined exit zero severity. + if any of the above severities reports > 0 results this method + returns True else it returns False + + :param exit_zero_severity: integer value converted from + exit_zero_severity_string + :return: bool value depending on the results + ''' + items_in_rankings = len(b_constants.RANKING) # this is the minimal level we shouldn't exit with 0 non_exit_zero_severity = exit_zero_severity + 1 @@ -339,6 +347,7 @@ def above_threshold_results(self, exit_zero_severity): return False + def _get_files_from_dir(files_dir, included_globs=None, excluded_path_strings=None): if not included_globs: diff --git a/tests/unit/cli/test_main.py b/tests/unit/cli/test_main.py index ef1031857..399f7b51a 100644 --- a/tests/unit/cli/test_main.py +++ b/tests/unit/cli/test_main.py @@ -294,7 +294,7 @@ def test_main_exit_with_results_and_with_exit_zero_flag(self): @mock.patch('sys.argv', ['bandit', '-c', 'bandit.yaml', 'test', '-o', 'output', '--exit-zero-severity', 'low']) - def test_main_exit_with_results_and_with_exit_zero_severity_flag_set_to_some_value_returning_true(self): + def test_main_exit_with_results_and_with_ezs_flag_set_returning_true(self): # Test that bandit exits with 0 on results and zero flag temp_directory = self.useFixture(fixtures.TempDir()).path os.chdir(temp_directory) @@ -303,23 +303,24 @@ def test_main_exit_with_results_and_with_exit_zero_severity_flag_set_to_some_val with mock.patch('bandit.core.manager.BanditManager.results_count' ) as mock_mgr_results_ct: mock_mgr_results_ct.return_value = 2 - with mock.patch('bandit.core.manager.BanditManager.above_threshold_results' - ) as mock_mgr_above_threshold_results_ct: + with mock.patch( + 'bandit.core.manager.BanditManager.above_threshold_results' + ) as mock_mgr_above_threshold_results_ct: mock_mgr_above_threshold_results_ct.return_value = True - self.assertRaisesRegex(SystemExit, '1', bandit.main) @mock.patch('sys.argv', ['bandit', '-c', 'bandit.yaml', 'test', '-o', 'output', '--exit-zero-severity', 'medium']) - def test_main_exit_with_results_and_with_exit_zero_severity_flag_set_to_some_value_returning_false(self): + def test_main_exit_with_results_and_with_ezs_flag_returning_false(self): # Test that bandit exits with 0 on results and zero flag temp_directory = self.useFixture(fixtures.TempDir()).path os.chdir(temp_directory) with open('bandit.yaml', 'wt') as fd: fd.write(bandit_config_content) - with mock.patch('bandit.core.manager.BanditManager.above_threshold_results' - ) as mock_mgr_above_threshold_results_ct: + with mock.patch( + 'bandit.core.manager.BanditManager.above_threshold_results' + ) as mock_mgr_above_threshold_results_ct: mock_mgr_above_threshold_results_ct.return_value = False self.assertRaisesRegex(SystemExit, '0', bandit.main) From 55340f3e00d861612e11a73702014345664d338d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Str=C3=B6mich?= Date: Sat, 12 Jun 2021 20:41:29 +0200 Subject: [PATCH 3/6] pylint check --- bandit/cli/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bandit/cli/main.py b/bandit/cli/main.py index d3a3bca1b..84739e640 100644 --- a/bandit/cli/main.py +++ b/bandit/cli/main.py @@ -270,7 +270,7 @@ def main(): help='control which severity makes bandit to exit with zero ' 'status code. Lower severities to the specified one are ' 'included implicitly' - ) + ) python_ver = sys.version.replace('\n', '') parser.add_argument( '--version', action='version', From d095329a5d27f974bfa1f5f64b418bee1acdf49f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Str=C3=B6mich?= Date: Sat, 12 Jun 2021 21:29:17 +0200 Subject: [PATCH 4/6] mutualy exclusive group for --exit-zero and --exit-zero-severity --- bandit/cli/main.py | 15 ++++++++++----- doc/source/man/bandit.rst | 6 ++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/bandit/cli/main.py b/bandit/cli/main.py index 84739e640..64f803c2d 100644 --- a/bandit/cli/main.py +++ b/bandit/cli/main.py @@ -261,15 +261,20 @@ def main(): '--ini', dest='ini_path', action='store', default=None, help='path to a .bandit file that supplies command line arguments' ) - parser.add_argument('--exit-zero', action='store_true', dest='exit_zero', - default=False, help='exit with 0, ' - 'even with results found') - parser.add_argument( + exit_zero_group = parser.add_mutually_exclusive_group(required=False) + exit_zero_group.add_argument( + '--exit-zero', action='store_true', dest='exit_zero', default=False, + help='exit with 0, even with results found' + ) + exit_zero_group.add_argument( '--exit-zero-severity', dest='exit_zero_severity_string', action='store', default=None, help='control which severity makes bandit to exit with zero ' 'status code. Lower severities to the specified one are ' - 'included implicitly' + 'included implicitly ' + '(low for LOW, ' + 'medium for MEDIUM, ' + 'high for HIGH).' ) python_ver = sys.version.replace('\n', '') parser.add_argument( diff --git a/doc/source/man/bandit.rst b/doc/source/man/bandit.rst index 037b86346..efa01e376 100644 --- a/doc/source/man/bandit.rst +++ b/doc/source/man/bandit.rst @@ -70,8 +70,10 @@ OPTIONS --ini INI_PATH path to a .bandit file that supplies command line arguments --exit-zero exit with 0, even with results found - --exit-zero-severity control which severity makes bandit to exit with zero status code. - Lower severities to the specified one are included implicitly + --exit-zero-severity EXIT_ZERO_SEVERITY_STRING + control which severity makes bandit to exit with zero status code. + Lower severities to the specified one are included implicitly + (low for LOW, medium for MEDIUM, high for HIGH). --version show program's version number and exit CUSTOM FORMATTING From 9f23c456088d44ec2ea41f049a61c18ffea5f65c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Str=C3=B6mich?= Date: Sun, 12 Dec 2021 14:32:13 +0100 Subject: [PATCH 5/6] Update bandit/cli/main.py Co-authored-by: Mikhail Feoktistov --- bandit/cli/main.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/bandit/cli/main.py b/bandit/cli/main.py index 64f803c2d..e4767dd00 100644 --- a/bandit/cli/main.py +++ b/bandit/cli/main.py @@ -268,13 +268,10 @@ def main(): ) exit_zero_group.add_argument( '--exit-zero-severity', dest='exit_zero_severity_string', - action='store', default=None, + action='store', default=None, choices=["all", "low", "medium", "high"], help='control which severity makes bandit to exit with zero ' 'status code. Lower severities to the specified one are ' - 'included implicitly ' - '(low for LOW, ' - 'medium for MEDIUM, ' - 'high for HIGH).' + 'included implicitly. ' ) python_ver = sys.version.replace('\n', '') parser.add_argument( From bbac4fe9dfd028d22d7be2b67241fa142e39cf63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Str=C3=B6mich?= Date: Sun, 12 Dec 2021 14:32:20 +0100 Subject: [PATCH 6/6] Update bandit/cli/main.py Co-authored-by: Mikhail Feoktistov --- bandit/cli/main.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/bandit/cli/main.py b/bandit/cli/main.py index e4767dd00..7ddcaa2bc 100644 --- a/bandit/cli/main.py +++ b/bandit/cli/main.py @@ -540,10 +540,8 @@ def main(): args.output_format, args.msg_template) - if args.exit_zero: - sys.exit(0) - - if ("exit_zero_severity" in args + if (args.exit_zero + or "exit_zero_severity" in args and not b_mgr.above_threshold_results(args.exit_zero_severity)): sys.exit(0)