Merge "Updated legacy profile support"
This commit is contained in:
commit
f2efa38c09
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
Loading…
x
Reference in New Issue
Block a user