From 7c4b9fa8b8d980a27c53000eb1961d436da7b223 Mon Sep 17 00:00:00 2001 From: Eric Brown Date: Thu, 13 Sep 2018 18:03:12 -0700 Subject: [PATCH] Add option -q, --quiet, --silent to hide output This commit adds a new command line argument to hide output when the result of the scan is passing with no warning/errors. The new option is -q or --quiet. It also allows --silent to be consistent with the GNU standard on CLI options [1]. [1] http://www.gnu.org/prep/standards/html_node/Option-Table.html#Option-Table Signed-off-by: Eric Brown Fixes Issue #384 --- README.rst | 4 +++- bandit/cli/main.py | 22 +++++++++++++------- bandit/core/manager.py | 4 +++- bandit/formatters/screen.py | 40 +++++++++++++++++++------------------ bandit/formatters/text.py | 39 +++++++++++++++++++----------------- doc/source/man/bandit.rst | 4 +++- tests/unit/cli/test_main.py | 4 ++-- 7 files changed, 68 insertions(+), 49 deletions(-) diff --git a/README.rst b/README.rst index 2311cf065..ed15d923c 100644 --- a/README.rst +++ b/README.rst @@ -93,7 +93,7 @@ Usage:: usage: bandit [-h] [-r] [-a {file,vuln}] [-n CONTEXT_LINES] [-c CONFIG_FILE] [-p PROFILE] [-t TESTS] [-s SKIPS] [-l] [-i] [-f {csv,custom,html,json,screen,txt,xml,yaml}] - [--msg-template MSG_TEMPLATE] [-o [OUTPUT_FILE]] [-v] [-d] + [--msg-template MSG_TEMPLATE] [-o [OUTPUT_FILE]] [-v] [-d] [-q] [--ignore-nosec] [-x EXCLUDED_PATHS] [-b BASELINE] [--ini INI_PATH] [--version] [targets [targets ...]] @@ -135,6 +135,8 @@ Usage:: -v, --verbose output extra information like excluded and included files -d, --debug turn on debug mode + -q, --quiet, --silent + only show output in the case of an error --ignore-nosec do not skip lines with # nosec comments -x EXCLUDED_PATHS, --exclude EXCLUDED_PATHS comma-separated list of paths to exclude from scan diff --git a/bandit/cli/main.py b/bandit/cli/main.py index ccec0ee90..7ce7255b3 100644 --- a/bandit/cli/main.py +++ b/bandit/cli/main.py @@ -32,16 +32,13 @@ LOG = logging.getLogger() -def _init_logger(debug=False, log_format=None): +def _init_logger(log_level=logging.INFO, log_format=None): '''Initialize the logger :param debug: Whether to enable debug mode :return: An instantiated logging instance ''' LOG.handlers = [] - log_level = logging.INFO - if debug: - log_level = logging.DEBUG if not log_format: # default log format @@ -136,7 +133,8 @@ def _log_info(args, profile): def main(): # bring our logging stuff up as early as possible - debug = ('-d' in sys.argv or '--debug' in sys.argv) + debug = (logging.DEBUG if '-d' in sys.argv or '--debug' in sys.argv else + logging.INFO) _init_logger(debug) extension_mgr = _init_extensions() @@ -218,7 +216,8 @@ def main(): type=argparse.FileType('w'), default=sys.stdout, help='write report to filename' ) - parser.add_argument( + group = parser.add_mutually_exclusive_group(required=False) + group.add_argument( '-v', '--verbose', dest='verbose', action='store_true', help='output extra information like excluded and included files' ) @@ -226,6 +225,10 @@ def main(): '-d', '--debug', dest='debug', action='store_true', help='turn on debug mode' ) + group.add_argument( + '-q', '--quiet', '--silent', dest='quiet', action='store_true', + help='only show output in the case of an error' + ) parser.add_argument( '--ignore-nosec', dest='ignore_nosec', action='store_true', help='do not skip lines with # nosec comments' @@ -254,6 +257,7 @@ def main(): parser.set_defaults(debug=False) parser.set_defaults(verbose=False) + parser.set_defaults(quiet=False) parser.set_defaults(ignore_nosec=False) plugin_info = ["%s\t%s" % (a[0], a[1].name) for a in @@ -332,7 +336,10 @@ def main(): # if the log format string was set in the options, reinitialize if b_conf.get_option('log_format'): log_format = b_conf.get_option('log_format') - _init_logger(debug, log_format=log_format) + _init_logger(log_level=logging.DEBUG, log_format=log_format) + + if args.quiet: + _init_logger(log_level=logging.WARN) try: profile = _get_profile(b_conf, args.profile, args.config_file) @@ -348,6 +355,7 @@ def main(): b_mgr = b_manager.BanditManager(b_conf, args.agg_type, args.debug, profile=profile, verbose=args.verbose, + quiet=args.quiet, ignore_nosec=args.ignore_nosec) if args.baseline is not None: diff --git a/bandit/core/manager.py b/bandit/core/manager.py index 0fd3c5f5b..48e80eb68 100644 --- a/bandit/core/manager.py +++ b/bandit/core/manager.py @@ -39,7 +39,7 @@ class BanditManager(object): scope = [] def __init__(self, config, agg_type, debug=False, verbose=False, - profile=None, ignore_nosec=False): + quiet=False, profile=None, ignore_nosec=False): '''Get logger, config, AST handler, and result store ready :param config: config options object @@ -47,12 +47,14 @@ def __init__(self, config, agg_type, debug=False, verbose=False, :param agg_type: aggregation type :param debug: Whether to show debug messages or not :param verbose: Whether to show verbose output + :param quiet: Whether to only show output in the case of an error :param profile_name: Optional name of profile to use (from cmd line) :param ignore_nosec: Whether to ignore #nosec or not :return: ''' self.debug = debug self.verbose = verbose + self.quiet = quiet if not profile: profile = {} self.ignore_nosec = ignore_nosec diff --git a/bandit/formatters/screen.py b/bandit/formatters/screen.py index 229c6c51e..173bdb91c 100644 --- a/bandit/formatters/screen.py +++ b/bandit/formatters/screen.py @@ -162,25 +162,27 @@ def report(manager, fileobj, sev_level, conf_level, lines=-1): """ bits = [] - bits.append(header("Run started:%s", datetime.datetime.utcnow())) - - if manager.verbose: - bits.append(get_verbose_details(manager)) - - bits.append(header("\nTest results:")) - bits.append(get_results(manager, sev_level, conf_level, lines)) - bits.append(header("\nCode scanned:")) - bits.append('\tTotal lines of code: %i' % - (manager.metrics.data['_totals']['loc'])) - - bits.append('\tTotal lines skipped (#nosec): %i' % - (manager.metrics.data['_totals']['nosec'])) - - bits.append(get_metrics(manager)) - skipped = manager.get_skipped() - bits.append(header("Files skipped (%i):", len(skipped))) - bits.extend(["\t%s (%s)" % skip for skip in skipped]) - do_print(bits) + issues = manager.get_issue_list(sev_level, conf_level) + if len(issues) or not manager.quiet: + bits.append(header("Run started:%s", datetime.datetime.utcnow())) + + if manager.verbose: + bits.append(get_verbose_details(manager)) + + bits.append(header("\nTest results:")) + bits.append(get_results(manager, sev_level, conf_level, lines)) + bits.append(header("\nCode scanned:")) + bits.append('\tTotal lines of code: %i' % + (manager.metrics.data['_totals']['loc'])) + + bits.append('\tTotal lines skipped (#nosec): %i' % + (manager.metrics.data['_totals']['nosec'])) + + bits.append(get_metrics(manager)) + skipped = manager.get_skipped() + bits.append(header("Files skipped (%i):", len(skipped))) + bits.extend(["\t%s (%s)" % skip for skip in skipped]) + do_print(bits) if fileobj.name != sys.stdout.name: LOG.info("Screen formatter output was not written to file: %s, " diff --git a/bandit/formatters/text.py b/bandit/formatters/text.py index bff914eed..1b94edc5d 100644 --- a/bandit/formatters/text.py +++ b/bandit/formatters/text.py @@ -141,29 +141,32 @@ def report(manager, fileobj, sev_level, conf_level, lines=-1): """ bits = [] - bits.append("Run started:%s" % datetime.datetime.utcnow()) + issues = manager.get_issue_list(sev_level, conf_level) + + if len(issues) or not manager.quiet: + bits.append("Run started:%s" % datetime.datetime.utcnow()) - if manager.verbose: - bits.append(get_verbose_details(manager)) + if manager.verbose: + bits.append(get_verbose_details(manager)) - bits.append("\nTest results:") - bits.append(get_results(manager, sev_level, conf_level, lines)) - bits.append("\nCode scanned:") - bits.append('\tTotal lines of code: %i' % - (manager.metrics.data['_totals']['loc'])) + bits.append("\nTest results:") + bits.append(get_results(manager, sev_level, conf_level, lines)) + bits.append("\nCode scanned:") + bits.append('\tTotal lines of code: %i' % + (manager.metrics.data['_totals']['loc'])) - bits.append('\tTotal lines skipped (#nosec): %i' % - (manager.metrics.data['_totals']['nosec'])) + bits.append('\tTotal lines skipped (#nosec): %i' % + (manager.metrics.data['_totals']['nosec'])) - skipped = manager.get_skipped() - bits.append(get_metrics(manager)) - bits.append("Files skipped (%i):" % len(skipped)) - bits.extend(["\t%s (%s)" % skip for skip in skipped]) - result = '\n'.join([bit for bit in bits]) + '\n' + skipped = manager.get_skipped() + bits.append(get_metrics(manager)) + bits.append("Files skipped (%i):" % len(skipped)) + bits.extend(["\t%s (%s)" % skip for skip in skipped]) + result = '\n'.join([bit for bit in bits]) + '\n' - with fileobj: - wrapped_file = utils.wrap_file_object(fileobj) - wrapped_file.write(utils.convert_file_contents(result)) + with fileobj: + wrapped_file = utils.wrap_file_object(fileobj) + wrapped_file.write(utils.convert_file_contents(result)) if fileobj.name != sys.stdout.name: LOG.info("Text output written to file: %s", fileobj.name) diff --git a/doc/source/man/bandit.rst b/doc/source/man/bandit.rst index f04596f78..01cf56ff3 100644 --- a/doc/source/man/bandit.rst +++ b/doc/source/man/bandit.rst @@ -8,7 +8,7 @@ SYNOPSIS bandit [-h] [-r] [-a {file,vuln}] [-n CONTEXT_LINES] [-c CONFIG_FILE] [-p PROFILE] [-t TESTS] [-s SKIPS] [-l] [-i] [-f {csv,custom,html,json,screen,txt,xml,yaml}] - [--msg-template MSG_TEMPLATE] [-o OUTPUT_FILE] [-v] [-d] + [--msg-template MSG_TEMPLATE] [-o OUTPUT_FILE] [-v] [-d] [-q] [--ignore-nosec] [-x EXCLUDED_PATHS] [-b BASELINE] [--ini INI_PATH] [--version] targets [targets ...] @@ -55,6 +55,8 @@ OPTIONS -v, --verbose output extra information like excluded and included files -d, --debug turn on debug mode + -q, --quiet, --silent + only show output in the case of an error --ignore-nosec do not skip lines with # nosec comments -x EXCLUDED_PATHS, --exclude EXCLUDED_PATHS comma-separated list of paths to exclude from scan diff --git a/tests/unit/cli/test_main.py b/tests/unit/cli/test_main.py index adc95cd78..30fa4931f 100644 --- a/tests/unit/cli/test_main.py +++ b/tests/unit/cli/test_main.py @@ -74,7 +74,7 @@ def tearDown(self): def test_init_logger(self): # Test that a logger was properly initialized - bandit._init_logger(False) + bandit._init_logger() self.assertIsNotNone(self.logger) self.assertNotEqual(self.logger.handlers, []) @@ -82,7 +82,7 @@ def test_init_logger(self): def test_init_logger_debug_mode(self): # Test that the logger's level was set at 'DEBUG' - bandit._init_logger(True) + bandit._init_logger(logging.DEBUG) self.assertEqual(logging.DEBUG, self.logger.level)