Allow certain command line arguments to be passed from file
This commit allows the use of a .bandit file, which will be in the ini format and used to pass command line arguments. This is useful for multiple projects which are running in the same gate, want to be able to exclude different files per-project, and aren't using tox. Change-Id: I4256bdb7df2416f3cc01798882fb7e2e229790a3
This commit is contained in:
parent
0959ab5312
commit
36f316ab82
26
README.rst
26
README.rst
|
@ -108,6 +108,8 @@ Usage::
|
|||
Path to a baseline report, in JSON format. Note:
|
||||
baseline reports must be output in one of the
|
||||
following formats: ['txt', 'html']
|
||||
--ini INI_PATH Path to a .bandit file which supplies command line
|
||||
arguments to Bandit.
|
||||
|
||||
The following plugin suites were discovered and loaded:
|
||||
any_other_function_with_shell_equals_true
|
||||
|
@ -172,6 +174,29 @@ Mac OSX:
|
|||
- /usr/local/etc/bandit/bandit.yaml
|
||||
- <path to venv>/bandit/config/bandit.yaml (if running within virtualenv)
|
||||
|
||||
Per Project Command Line Args
|
||||
-----------------------------
|
||||
Projects may include a `.bandit` file that specifies command line arguments
|
||||
that should be supplied for that project. The currently supported arguments
|
||||
are:
|
||||
|
||||
- exclude: comma separated list of excluded paths
|
||||
- skips: comma separated list of tests to skip
|
||||
- tests: comma separated list of tests to run
|
||||
|
||||
To use this, put a .bandit file in your project's directory. For example:
|
||||
|
||||
::
|
||||
|
||||
[bandit]
|
||||
exclude: /test
|
||||
|
||||
::
|
||||
|
||||
[bandit]
|
||||
tests: B101,B102,B301
|
||||
|
||||
|
||||
Exclusions
|
||||
----------
|
||||
In the event that a line of code triggers a Bandit issue, but that the line
|
||||
|
@ -223,6 +248,7 @@ To write a test:
|
|||
vulnerability might present itself and extend the example file and the test
|
||||
function accordingly.
|
||||
|
||||
|
||||
Extending Bandit
|
||||
----------------
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
from __future__ import absolute_import
|
||||
|
||||
import argparse
|
||||
import fnmatch
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
@ -59,11 +60,54 @@ def _init_logger(debug=False, log_format=None):
|
|||
logger.debug("logging initialized")
|
||||
|
||||
|
||||
def _get_options_from_ini(ini_path, target):
|
||||
"""Return a dictionary of config options or None if we can't load any."""
|
||||
ini_file = None
|
||||
|
||||
if ini_path:
|
||||
ini_file = ini_path
|
||||
else:
|
||||
bandit_files = []
|
||||
|
||||
for t in target:
|
||||
for root, dirnames, filenames in os.walk(t):
|
||||
for filename in fnmatch.filter(filenames, '.bandit'):
|
||||
bandit_files.append(os.path.join(root, filename))
|
||||
|
||||
if len(bandit_files) > 1:
|
||||
logger.error('Multiple .bandit files found - scan separately or '
|
||||
'choose one with --ini\n\t%s',
|
||||
', '.join(bandit_files))
|
||||
sys.exit(2)
|
||||
|
||||
elif len(bandit_files) == 1:
|
||||
ini_file = bandit_files[0]
|
||||
logger.info('Found project level .bandit file: %s',
|
||||
bandit_files[0])
|
||||
|
||||
if ini_file:
|
||||
return utils.parse_ini_file(ini_file)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def _init_extensions():
|
||||
from bandit.core import extension_loader as ext_loader
|
||||
return ext_loader.MANAGER
|
||||
|
||||
|
||||
def _log_option_source(arg_val, ini_val, option_name):
|
||||
"""It's useful to show the source of each option."""
|
||||
if arg_val:
|
||||
logger.info("Using command line arg for %s", option_name)
|
||||
return arg_val
|
||||
elif ini_val:
|
||||
logger.info("Using .bandit arg for %s", option_name)
|
||||
return ini_val
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def _running_under_virtualenv():
|
||||
if hasattr(sys, 'real_prefix'):
|
||||
return True
|
||||
|
@ -192,6 +236,11 @@ def main():
|
|||
'Note: baseline reports must be output in one of '
|
||||
'the following formats: ' + str(baseline_formatters)
|
||||
)
|
||||
parser.add_argument(
|
||||
'--ini', dest='ini_path', action='store', default=None,
|
||||
help='Path to a .bandit file which supplies command line arguments to '
|
||||
'Bandit.'
|
||||
)
|
||||
parser.set_defaults(debug=False)
|
||||
parser.set_defaults(verbose=False)
|
||||
parser.set_defaults(ignore_nosec=False)
|
||||
|
@ -216,6 +265,21 @@ def main():
|
|||
logger.error('%s', e)
|
||||
sys.exit(2)
|
||||
|
||||
# Handle .bandit files in projects to pass cmdline args from file
|
||||
ini_options = _get_options_from_ini(args.ini_path, args.targets)
|
||||
if ini_options:
|
||||
# prefer command line, then ini file
|
||||
args.excluded_paths = _log_option_source(args.excluded_paths,
|
||||
ini_options.get('exclude'),
|
||||
'excluded paths')
|
||||
|
||||
args.skips = _log_option_source(args.skips, ini_options.get('skips'),
|
||||
'skipped tests')
|
||||
|
||||
args.tests = _log_option_source(args.tests, ini_options.get('tests'),
|
||||
'selected tests')
|
||||
# TODO(tmcpeak): any other useful options to pass from .bandit?
|
||||
|
||||
# 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')
|
||||
|
|
|
@ -21,6 +21,10 @@ import logging
|
|||
import os.path
|
||||
import sys
|
||||
|
||||
try:
|
||||
import configparser
|
||||
except ImportError:
|
||||
import ConfigParser as configparser
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -329,3 +333,16 @@ def get_path_for_function(f):
|
|||
else:
|
||||
logger.warn("Cannot resolve file path for module %s", module_name)
|
||||
return None
|
||||
|
||||
|
||||
def parse_ini_file(f_loc):
|
||||
config = configparser.ConfigParser()
|
||||
try:
|
||||
config.read(f_loc)
|
||||
return {k: v for k, v in config.items('bandit')}
|
||||
|
||||
except (configparser.Error, KeyError, TypeError):
|
||||
logger.warning("Unable to parse config file %s or missing [bandit] "
|
||||
"section", f_loc)
|
||||
|
||||
return None
|
||||
|
|
|
@ -272,3 +272,21 @@ class UtilTests(testtools.TestCase):
|
|||
self.assertEqual('deep value', b_utils.deepgetattr(a, 'b.c.d'))
|
||||
self.assertEqual('deep value 2', b_utils.deepgetattr(a, 'b.c.d2'))
|
||||
self.assertRaises(AttributeError, b_utils.deepgetattr, a.b, 'z')
|
||||
|
||||
def test_parse_ini_file(self):
|
||||
|
||||
tests = [{'content': "[bandit]\nexclude=/abc,/def",
|
||||
'expected': {'exclude': '/abc,/def'}},
|
||||
|
||||
{'content': '[Blabla]\nsomething=something',
|
||||
'expected': None}]
|
||||
|
||||
with tempfile.NamedTemporaryFile('r+') as t:
|
||||
for test in tests:
|
||||
f = open(t.name, 'w')
|
||||
f.write(test['content'])
|
||||
f.close()
|
||||
|
||||
self.assertEqual(b_utils.parse_ini_file(t.name),
|
||||
test['expected'])
|
||||
|
||||
|
|
Loading…
Reference in New Issue