Making config optional

This commit removes the requirement for having a config file.
Sometimes projects may want to use one to list out tests, define
a profile, or override a setting, but they are no longer required.

Change-Id: I6e467f58b2b27cae647901ac2c3f75a764e74c0c
This commit is contained in:
Travis McPeak 2016-02-10 15:55:43 -08:00
parent 67ed78447c
commit 54a06aaebb
10 changed files with 43 additions and 218 deletions

View File

@ -98,8 +98,8 @@ Usage::
max number of code lines to display for each issue
identified
-c CONFIG_FILE, --configfile CONFIG_FILE
if omitted default locations are checked. Check
documentation for searched paths
optional config file to use for selecting plugins and
overriding defaults
-p PROFILE, --profile PROFILE
test set profile in config to use (defaults to all
tests)
@ -193,32 +193,12 @@ Usage::
Configuration
-------------
The Bandit config file is used to set several things, including:
- profiles - defines group of tests which should or shouldn't be run
An optional config file may be supplied and may include:
- lists of tests which should or shouldn't be run
- exclude_dirs - sections of the path, that if matched, will be excluded from
scanning
- plugin configs - used to tune plugins, for example: by tuning
blacklist_imports, you can set which imports should be flagged
- other - plugins directory, included file types, shell display
colors, etc.
Bandit requires a config file which can be specified on the command line via
-c/--configfile. If this is not provided Bandit will search for a default
config file (bandit.yaml) in the following preference order:
GNU/Linux:
- ./bandit.yaml
- ~/.config/bandit/bandit.yaml
- /etc/bandit/bandit.yaml
- /usr/local/etc/bandit/bandit.yaml
- <path to venv>/etc/bandit/bandit.yaml (if running within virtualenv)
Mac OSX:
- ./bandit.yaml
- /Users/${USER}/Library/Application Support/bandit/bandit.yaml
- /Library/Application Support/bandit/bandit.yaml
- /usr/local/etc/bandit/bandit.yaml
- <path to venv>/bandit/config/bandit.yaml (if running within virtualenv)
- overridden plugin settings - may provide different settings for some
plugins
Per Project Command Line Args
-----------------------------

View File

@ -18,9 +18,7 @@ import fnmatch
import logging
import os
import sys
import sysconfig
import appdirs
import six
import bandit
@ -115,40 +113,10 @@ def _running_under_virtualenv():
return True
def _find_config():
# prefer config file in the following order:
# 1) current directory, 2) user home directory, 3) bundled config
config_dirs = (
['.'] + [appdirs.user_config_dir("bandit")] +
appdirs.site_config_dir("bandit", multipath=True).split(':'))
if _running_under_virtualenv():
config_dirs.append(os.path.join(sys.prefix, 'etc', 'bandit'))
config_dirs.append(
os.path.join(sysconfig.get_paths().get('purelib', ''),
'bandit', 'config'))
config_locations = [os.path.join(s, BASE_CONFIG) for s in config_dirs]
# pip on Mac installs to the following path, but appdirs expects to
# follow Mac's BPFileSystem spec which doesn't include this path so
# we'll insert it. Issue raised as http://git.io/vOreU
mac_pip_cfg_path = "/usr/local/etc/bandit/bandit.yaml"
if mac_pip_cfg_path not in config_locations:
config_locations.append(mac_pip_cfg_path)
for config_file in config_locations:
if os.path.isfile(config_file):
return config_file # Found a valid config
else:
# Failed to find any config, raise an error.
raise utils.NoConfigFileFound(config_locations)
def main():
# bring our logging stuff up as early as possible
debug = ('-d' in sys.argv or '--debug' in sys.argv)
_init_logger(debug)
# By default path would be /etx/xdg/bandit, we want system paths
os.environ['XDG_CONFIG_DIRS'] = '/etc:/usr/local/etc'
extension_mgr = _init_extensions()
baseline_formatters = [f.name for f in filter(lambda x:
@ -183,8 +151,8 @@ def main():
parser.add_argument(
'-c', '--configfile', dest='config_file',
action='store', default=None, type=str,
help=('if omitted default locations are checked. '
'Check documentation for searched paths')
help=('optional config file to use for selecting plugins and '
'overriding defaults')
)
group = parser.add_mutually_exclusive_group()
group.add_argument(
@ -274,16 +242,9 @@ def main():
# setup work - parse arguments, and initialize BanditManager
args = parser.parse_args()
config_file = args.config_file
if not config_file:
try:
config_file = _find_config()
except utils.NoConfigFileFound as e:
logger.error(e)
sys.exit(2)
try:
b_conf = b_config.BanditConfig(config_file)
b_conf = b_config.BanditConfig(config_file=args.config_file)
except (utils.ConfigFileUnopenable, utils.ConfigFileInvalidYaml) as e:
logger.error('%s', e)
sys.exit(2)
@ -353,7 +314,9 @@ def main():
sys.exit(2)
if args.output_format != "json":
logger.info("using config: %s", config_file)
if args.config_file:
logger.info("using config: %s", args.config_file)
logger.info("running on Python %d.%d.%d", sys.version_info.major,
sys.version_info.minor, sys.version_info.micro)

View File

@ -33,7 +33,7 @@ class BanditConfig():
'B001' # Built in blacklist test
]
def __init__(self, config_file):
def __init__(self, config_file=None):
'''Attempt to initialize a config dictionary from a yaml file.
Error out if loading the yaml file fails for any reason.
@ -46,20 +46,31 @@ class BanditConfig():
'''
self.config_file = config_file
try:
f = open(config_file, 'r')
except IOError:
raise utils.ConfigFileUnopenable(config_file)
self._config = {}
try:
self._config = yaml.safe_load(f)
except yaml.YAMLError:
raise utils.ConfigFileInvalidYaml(config_file)
if config_file:
try:
f = open(config_file, 'r')
except IOError:
raise utils.ConfigFileUnopenable(config_file)
try:
self._config = yaml.safe_load(f)
except yaml.YAMLError:
raise utils.ConfigFileInvalidYaml(config_file)
# valid config must be a dict
if not isinstance(self._config, dict):
raise utils.ConfigFileInvalidYaml(config_file)
if self._config:
self.convert_legacy_config()
self.validate_profiles()
else:
# use sane defaults
self._config['plugin_name_pattern'] = '*.py'
self._config['include'] = ['*.py', '*.pyw']
self.validate_profiles()
self._init_settings()
def get_option(self, option_string):

View File

@ -79,7 +79,9 @@ class BanditManager():
self.scores = []
def _get_profile(self, profile_name):
if profile_name not in self.b_conf.config['profiles']:
if(not self.b_conf.get_option('profiles') or
profile_name not in self.b_conf.config['profiles']):
raise utils.ProfileNotFound(self.b_conf.config_file, profile_name)
profile = self.b_conf.config['profiles'][profile_name]

View File

@ -121,13 +121,6 @@ class InvalidModulePath(Exception):
pass
class NoConfigFileFound(Exception):
def __init__(self, config_locations):
message = ("no config found - tried: " +
", ".join(config_locations))
super(NoConfigFileFound, self).__init__(message)
class ConfigFileUnopenable(Exception):
"""Raised when the config file cannot be opened."""
def __init__(self, config_file):

View File

@ -1,7 +1,6 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
appdirs>=1.3.0 # MIT License
GitPython>=1.0.1 # BSD License (3 clause)
PyYAML>=3.1.0 # MIT
six>=1.9.0 # MIT

View File

@ -36,8 +36,8 @@ profiles:
- start_process_with_a_shell
shell_injection:
subprocess:
subprocess: []
no_shell: []
shell:
- os.system
"""
@ -102,7 +102,8 @@ class BanditBaselineToolTests(testtools.TestCase):
'benign_two.py'],
'expected_return': 0}]
baseline_command = ['bandit-baseline', '-r', '.', '-p', 'test']
baseline_command = ['bandit-baseline', '-c', 'bandit.yaml', '-r', '.',
'-p', 'test']
for branch in branches:
branch['branch'] = git_repo.create_head(branch['name'])

View File

@ -156,15 +156,6 @@ class BanditCLIMainTests(testtools.TestCase):
option_name = 'aggregate'
self.assertIsNone(bandit._log_option_source(None, None, option_name))
@patch('sys.argv', ['bandit', 'test'])
def test_main_no_config(self):
# Test that bandit exits when a config file cannot be found, raising a
# NoConfigFileFound error
with patch('bandit.cli.main._find_config') as mock_find_config:
mock_find_config.side_effect = utils.NoConfigFileFound('')
# assert a SystemExit with code 2
self.assertRaisesRegex(SystemExit, '2', bandit.main)
@patch('sys.argv', ['bandit', '-c', 'bandit.yaml', 'test'])
def test_main_config_unopenable(self):
# Test that bandit exits when a config file cannot be opened
@ -299,32 +290,3 @@ class BanditCLIMainTests(testtools.TestCase):
mock_mgr_results_ct.return_value = 0
# assert a SystemExit with code 0
self.assertRaisesRegex(SystemExit, '0', bandit.main)
class BanditCLIMainFindConfigTests(testtools.TestCase):
def setUp(self):
super(BanditCLIMainFindConfigTests, self).setUp()
self.current_directory = os.getcwd()
def tearDown(self):
super(BanditCLIMainFindConfigTests, self).tearDown()
os.chdir(self.current_directory)
def test_find_config_no_config(self):
# Test that a utils.NoConfigFileFound error is raised when no config
# file is found
with patch('os.path.isfile') as mock_os_path_isfile:
# patch to make sure no config files can be found
mock_os_path_isfile.return_value = False
self.assertRaises(utils.NoConfigFileFound, bandit._find_config)
def test_find_config_local_config(self):
# Test that when a config file is found is current directory, it is
# used as the config file
temp_directory = self.useFixture(fixtures.TempDir()).path
os.chdir(temp_directory)
local_config = "./bandit.yaml"
with open(local_config, 'wt') as fd:
fd.write(bandit_config_content)
self.assertEqual(local_config, bandit._find_config())

View File

@ -109,7 +109,8 @@ class TestGetOption(testtools.TestCase):
class TestGetSetting(testtools.TestCase):
def setUp(self):
super(TestGetSetting, self).setUp()
f = self.useFixture(TempFile())
test_yaml = 'key: value'
f = self.useFixture(TempFile(test_yaml))
self.b_config = config.BanditConfig(f.name)
def test_not_exist(self):

View File

@ -1,87 +0,0 @@
# Copyright (c) 2015 VMware, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import fixtures
import testtools
from bandit.cli import main as bandit
from bandit.core import utils
BASE_CONFIG = '/bandit.yaml'
class FindConfigTests(testtools.TestCase):
def _test(self, directory_with_config=None, user_config_dir=None,
site_config_dir=None):
user_config_dir = user_config_dir or self.getUniqueString()
site_config_dir = site_config_dir or self.getUniqueString()
self.useFixture(
fixtures.MockPatch('appdirs.user_config_dir',
return_value=user_config_dir))
self.useFixture(
fixtures.MockPatch('appdirs.site_config_dir',
return_value=site_config_dir))
def is_current_config(arg):
if not directory_with_config:
return False
return arg == (directory_with_config + BASE_CONFIG)
self.useFixture(
fixtures.MockPatch('os.path.isfile', is_current_config))
found_config = bandit._find_config()
exp_config = directory_with_config + BASE_CONFIG
self.assertEqual(exp_config, found_config)
def test_current_directory(self):
# the config file in the current directory is returned if it's there.
self._test(directory_with_config='.')
def test_user_home_directory(self):
# the config file in the user home directory is returned if there isn't
# one in the current directory.
user_config_dir = self.getUniqueString()
self._test(directory_with_config=user_config_dir,
user_config_dir=user_config_dir)
def test_bundled_config(self):
# the bundled config file is returned if there isn't one in the current
# directory or user home directory.
site_config_dir = self.getUniqueString()
self._test(directory_with_config=site_config_dir,
site_config_dir=site_config_dir)
def test_mac_pip_cfg_path(self):
# pip on Mac installs to /usr/local/etc/bandit/bandit.yaml and that's
# checked, too. See issue at http://git.io/vOreU
self._test(directory_with_config='/usr/local/etc/bandit')
def test_not_found(self):
# NoConfigFileFound is raised if there's no config in any location.
self.assertRaises(utils.NoConfigFileFound, self._test)
def test_bundled_config_split(self):
# the bundled config can return a path separated by : and all those
# paths are searched in order.
dirs = [self.getUniqueString(), self.getUniqueString()]
site_config_dirs = ':'.join(dirs)
# Check all the directories
for dir in dirs:
self._test(directory_with_config=dir,
site_config_dir=site_config_dirs)