Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Commit

Permalink
Allow certain command line arguments to be passed from file
Browse files Browse the repository at this point in the history
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

Conflicts:
	README.rst
	tests/unit/core/test_util.py
  • Loading branch information
Travis McPeak committed Jan 26, 2016
1 parent 353634f commit c364408
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 1 deletion.
27 changes: 26 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ Usage::
-b BASELINE, --baseline BASELINE
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
Expand Down Expand Up @@ -172,6 +173,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
Expand Down Expand Up @@ -223,6 +247,7 @@ To write a test:
vulnerability might present itself and extend the example file and the test
function accordingly.


Extending Bandit
----------------

Expand Down
64 changes: 64 additions & 0 deletions bandit/bandit.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from __future__ import absolute_import

import argparse
import fnmatch
import logging
import os
import sys
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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')
Expand Down
17 changes: 17 additions & 0 deletions bandit/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
import os.path
import sys

try:
import configparser
except ImportError:
import ConfigParser as configparser

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -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
17 changes: 17 additions & 0 deletions tests/unit/core/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,3 +272,20 @@ def test_deepgetattr(self):
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'])

0 comments on commit c364408

Please sign in to comment.