Merge "Updated legacy profile support"

This commit is contained in:
Jenkins 2016-03-14 15:45:55 +00:00 committed by Gerrit Code Review
commit f2efa38c09
6 changed files with 76 additions and 61 deletions

View File

@ -77,7 +77,7 @@ Usage::
$ bandit -h
usage: bandit [-h] [-r] [-a {file,vuln}] [-n CONTEXT_LINES] [-c CONFIG_FILE]
[-p PROFILE | -t TESTS | -s SKIPS] [-l] [-i]
[-p PROFILE] [-t TESTS] [-s SKIPS] [-l] [-i]
[-f {csv,html,json,screen,txt,xml}] [-o OUTPUT_FILE] [-v] [-d]
[--ignore-nosec] [-x EXCLUDED_PATHS] [-b BASELINE]
[--ini INI_PATH] [--version]
@ -104,9 +104,9 @@ Usage::
test set profile in config to use (defaults to all
tests)
-t TESTS, --tests TESTS
list of test names to run
comma separated list of test IDs to run
-s SKIPS, --skip SKIPS
list of test names to skip
comma separated list of test IDs to skip
-l, --level results severity filter. Show only issues of a given
severity level or higher. -l for LOW, -ll for MEDIUM,
-lll for HIGH

View File

@ -19,22 +19,22 @@ import logging
import os
import sys
from stevedore import extension
import yaml
from bandit.core import extension_loader
PROG_NAME = 'bandit_conf_generator'
logger = logging.getLogger(__name__)
template = """
### profile may optionally select or skip tests
### config may optionally select or skip tests
# (optional) list included tests here:
# tests: B101,B102
{test}
# (optional) list skipped tests here:
# skip: B201, B202
{skip}
### override settings - used to set settings for plugins to non-default values
@ -69,29 +69,28 @@ def parse_args():
description=help_description,
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('-s', '--show-defaults', dest='show_defaults',
parser.add_argument('--show-defaults', dest='show_defaults',
action='store_true',
help='show the default settings values for each '
'plugin but do not output a profile')
parser.add_argument('-o', '--out', dest='output_file',
action='store',
help='output file to save profile')
parser.add_argument(
'-t', '--tests', dest='tests',
action='store', default=None, type=str,
help='list of test names to run')
parser.add_argument(
'-s', '--skip', dest='skips',
action='store', default=None, type=str,
help='list of test names to skip')
args = parser.parse_args()
return args
def get_config_settings():
"""Print list of all plugins and default values of config."""
plugins_mgr = extension.ExtensionManager(namespace='bandit.plugins',
invoke_on_load=False,
verify_requirements=False)
logger.info('Successfully discovered %d plugins',
len(plugins_mgr.extensions))
config = {}
for plugin in plugins_mgr.extensions:
for plugin in extension_loader.MANAGER.plugins:
fn_name = plugin.name
function = plugin.plugin
@ -122,12 +121,29 @@ def main():
try:
with open(args.output_file, 'w') as f:
contents = template.format(settings=yaml_settings)
skips = args.skips.split(',') if args.skips else []
tests = args.tests.split(',') if args.tests else []
for skip in skips:
if not extension_loader.MANAGER.check_id(skip):
raise RuntimeError('unknown ID in skips: %s' % skip)
for test in tests:
if not extension_loader.MANAGER.check_id(test):
raise RuntimeError('unknown ID in tests: %s' % test)
contents = template.format(
settings=yaml_settings,
skip='skips: ' + str(skips) if skips else '',
test='tests: ' + str(tests) if tests else '')
f.write(contents)
except IOError:
logger.error("Unable to open %s for writing", args.output_file)
except Exception as e:
logger.error("Error: %s", e)
else:
logger.info("Successfully wrote profile: %s", args.output_file)

View File

@ -120,13 +120,20 @@ def _get_profile(config, profile_name, config_path):
profile = profiles.get(profile_name)
if profile is None:
raise utils.ProfileNotFound(config_path, profile_name)
if profile:
logger.debug("read in profile '%s': %s", profile_name, profile)
logger.debug("read in legacy profile '%s': %s", profile_name, profile)
else:
profile['include'] = set(config.get_option('tests') or [])
profile['exclude'] = set(config.get_option('skips') or [])
return profile
def _log_info(args, profile):
logger.info("profile include tests: %s", profile['include'])
logger.info("profile exculde tests: %s", profile['exclude'])
logger.info("cli include tests: %s", args.tests)
logger.info("cli exculde tests: %s", args.skips)
def main():
# bring our logging stuff up as early as possible
debug = ('-d' in sys.argv or '--debug' in sys.argv)
@ -168,21 +175,20 @@ def main():
help=('optional config file to use for selecting plugins and '
'overriding defaults')
)
group = parser.add_mutually_exclusive_group()
group.add_argument(
parser.add_argument(
'-p', '--profile', dest='profile',
action='store', default=None, type=str,
help='test set profile in config to use (defaults to all tests)'
)
group.add_argument(
parser.add_argument(
'-t', '--tests', dest='tests',
action='store', default=None, type=str,
help='list of test names to run'
help='comma separated list of test IDs to run'
)
group.add_argument(
parser.add_argument(
'-s', '--skip', dest='skips',
action='store', default=None, type=str,
help='list of test names to skip'
help='comma separated list of test IDs to skip'
)
parser.add_argument(
'-l', '--level', dest='severity', action='count',
@ -286,15 +292,15 @@ def main():
try:
profile = _get_profile(b_conf, args.profile, args.config_file)
if not profile:
profile = {'include': args.tests.split(',') if args.tests else [],
'exclude': args.skips.split(',') if args.skips else []}
profile['include'].update(args.tests.split(',') if args.tests else [])
profile['exclude'].update(args.skips.split(',') if args.skips else [])
extension_mgr.validate_profile(profile)
except (utils.ProfileNotFound, ValueError) as e:
logger.error(e)
sys.exit(2)
_log_info(args, profile)
b_mgr = b_manager.BanditManager(b_conf, args.agg_type, args.debug,
profile=profile, verbose=args.verbose,
ignore_nosec=args.ignore_nosec)

View File

@ -89,18 +89,12 @@ class Manager(object):
def validate_profile(self, profile):
'''Validate that everything in the configured profiles looks good.'''
def _check(test):
return (
test not in self.plugins_by_id and
test not in self.blacklist_by_id and
test not in self.builtin)
for inc in profile['include']:
if _check(inc):
if not self.check_id(inc):
raise ValueError('Unknown Test found in profile: %s' % inc)
for exc in profile['exclude']:
if _check(exc):
if not self.check_id(exc):
raise ValueError('Unknown Test found in profile: %s' % exc)
union = set(profile['include']) & set(profile['exclude'])
@ -108,6 +102,11 @@ class Manager(object):
raise ValueError('None exclusive include/excule test sets: %s' %
union)
def check_id(self, test):
return (
test in self.plugins_by_id or
test in self.blacklist_by_id or
test in self.builtin)
# Using entry-points and pkg_resources *can* be expensive. So let's load these
# once, store them on the object, and have a module global object for

View File

@ -14,13 +14,15 @@
# License for the specific language governing permissions and limitations
# under the License.
import importlib
import logging
import mock
from stevedore import extension
import testtools
import yaml
from bandit.cli import config_generator
from bandit.core import extension_loader
from bandit.core import test_properties as test
@ -57,21 +59,6 @@ class BanditConfigGeneratorLoggerTests(testtools.TestCase):
class BanditConfigGeneratorTests(testtools.TestCase):
def _make_test_manager(self, plugin):
return extension.ExtensionManager.make_test_instance(
[extension.Extension('test', None, _test_plugin, None)])
def setUp(self):
mngr = self._make_test_manager(mock.MagicMock)
self.patchExtMan = mock.patch('stevedore.extension.ExtensionManager')
self.mockExtMan = self.patchExtMan.start()
self.mockExtMan.return_value = mngr
super(BanditConfigGeneratorTests, self).setUp()
def tearDown(self):
super(BanditConfigGeneratorTests, self).tearDown()
self.patchExtMan.stop()
@mock.patch('sys.argv', ['bandit-config-generator'])
def test_parse_args_no_defaults(self):
# Test that the config generator does not show default plugin settings
@ -91,8 +78,15 @@ class BanditConfigGeneratorTests(testtools.TestCase):
self.assertEqual('dummyfile', return_value.output_file)
def test_get_config_settings(self):
config = {}
for plugin in extension_loader.MANAGER.plugins:
function = plugin.plugin
if hasattr(plugin.plugin, '_takes_config'):
module = importlib.import_module(function.__module__)
config[plugin.name] = module.gen_config(
function._takes_config)
settings = config_generator.get_config_settings()
self.assertEqual(settings, "test: {test: test data}\n")
self.assertEqual(yaml.safe_dump(config), settings)
@mock.patch('sys.argv', ['bandit-config-generator', '--show-defaults'])
def test_main_show_defaults(self):

View File

@ -45,13 +45,13 @@ def test_plugin():
'Calls': sets}
class BanditTesSetTests(testtools.TestCase):
class BanditTestSetTests(testtools.TestCase):
def _make_test_manager(self, plugin):
return extension.ExtensionManager.make_test_instance(
[extension.Extension('test_plugin', None, test_plugin, None)])
def setUp(self):
super(BanditTesSetTests, self).setUp()
super(BanditTestSetTests, self).setUp()
mngr = self._make_test_manager(mock.MagicMock)
self.patchExtMan = mock.patch('stevedore.extension.ExtensionManager')
self.mockExtMan = self.patchExtMan.start()
@ -63,7 +63,7 @@ class BanditTesSetTests(testtools.TestCase):
def tearDown(self):
self.patchExtMan.stop()
super(BanditTesSetTests, self).tearDown()
super(BanditTestSetTests, self).tearDown()
extension_loader.MANAGER = self.old_ext_man
def test_has_defaults(self):