Allow creation of test profiles and switch config to yaml
This commit is contained in:
parent
854b124529
commit
bb2f7cae4d
|
@ -43,8 +43,10 @@ Usage:
|
|||
-h, --help show this help message and exit
|
||||
-C CONTEXT_LINES, --context CONTEXT_LINES
|
||||
number of context lines to print
|
||||
-t TEST_CONFIG, --testconfig TEST_CONFIG
|
||||
test config file (default: bandit.ini)
|
||||
-f CONFIG_FILE, --configfile TEST_CONFIG
|
||||
test config file (default: bandit.yaml)
|
||||
-p PROFILE_NAME, --profile PROFILE_NAME
|
||||
run using specified test profile
|
||||
-l, --level results level filter
|
||||
-o OUTPUT_FILE, --output OUTPUT_FILE
|
||||
write report to filename
|
||||
|
|
17
bandit.ini
17
bandit.ini
|
@ -1,17 +0,0 @@
|
|||
[Import]
|
||||
import_name_match = test_imports
|
||||
import_name_telnetlib = test_imports
|
||||
|
||||
[ImportFrom]
|
||||
import_name_match = test_imports
|
||||
|
||||
[Call]
|
||||
call_bad_names = test_calls
|
||||
call_subprocess_popen = test_calls
|
||||
call_shell_true = test_calls
|
||||
call_no_cert_validation = test_calls
|
||||
call_bad_permissions = test_calls
|
||||
call_wildcard_injection = test_calls
|
||||
|
||||
[Str]
|
||||
str_hardcoded_bind_all_interfaces = test_strs
|
|
@ -0,0 +1,6 @@
|
|||
profiles:
|
||||
ShellInjection:
|
||||
include:
|
||||
- call_subprocess_popen
|
||||
- call_shell_true
|
||||
exclude:
|
|
@ -14,6 +14,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import config
|
||||
import context
|
||||
import manager
|
||||
import meta_ast
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
#
|
||||
# Copyright 2014 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# 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 sys
|
||||
import yaml
|
||||
|
||||
|
||||
class BanditConfig():
|
||||
|
||||
_config = dict()
|
||||
|
||||
def __init__(self, logger, config_file):
|
||||
'''
|
||||
Attempt to initialize a config dictionary from a yaml file, error out
|
||||
if this fails for any reason.
|
||||
:param logger: Logger to be used in the case of errors
|
||||
:param config_file: The Bandit yaml config file
|
||||
:return: -
|
||||
'''
|
||||
try:
|
||||
f = open(config_file, 'r')
|
||||
except IOError:
|
||||
logger.error("could not open config file: %s" % config_file)
|
||||
sys.exit(2)
|
||||
else:
|
||||
# yaml parser does its own exception handling
|
||||
self._config = yaml.load(f)
|
||||
|
||||
@property
|
||||
def config(self):
|
||||
'''
|
||||
Property to return the config dictionary
|
||||
:return: Config dictionary
|
||||
'''
|
||||
return self._config
|
|
@ -17,6 +17,7 @@
|
|||
import _ast
|
||||
from bandit import utils
|
||||
|
||||
|
||||
class Context():
|
||||
def __init__(self, context_object=None):
|
||||
'''
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
import sys
|
||||
import logging
|
||||
import ast
|
||||
from bandit import config as b_config
|
||||
from bandit import result_store as b_result_store
|
||||
from bandit import node_visitor as b_node_visitor
|
||||
from bandit import test_set as b_test_set
|
||||
|
@ -29,11 +30,24 @@ class BanditManager():
|
|||
scope = []
|
||||
progress = 50
|
||||
|
||||
def __init__(self, test_config, debug=False):
|
||||
def __init__(self, config_file, debug=False, profile_name=None):
|
||||
self.logger = self._init_logger(debug)
|
||||
self.b_ma = b_meta_ast.BanditMetaAst(self.logger)
|
||||
self.b_rs = b_result_store.BanditResultStore(self.logger)
|
||||
self.b_ts = b_test_set.BanditTestSet(self.logger, test_config)
|
||||
self.b_conf = b_config.BanditConfig(self.logger, config_file)
|
||||
|
||||
# if the profile name was specified, try to find it in the config
|
||||
if profile_name:
|
||||
if profile_name in self.b_conf.config['profiles']:
|
||||
profile = self.b_conf.config['profiles'][profile_name]
|
||||
else:
|
||||
self.logger.error('unable to find profile (%s) in config file: '
|
||||
'%s' % (profile_name, config_file))
|
||||
sys.exit(2)
|
||||
else:
|
||||
profile = None
|
||||
|
||||
self.b_ts = b_test_set.BanditTestSet(self.logger, profile=profile)
|
||||
|
||||
def get_logger(self):
|
||||
return self.logger
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
# under the License.
|
||||
|
||||
|
||||
import copy
|
||||
import sys
|
||||
from collections import OrderedDict
|
||||
import glob
|
||||
|
@ -22,14 +23,78 @@ from inspect import getmembers, isfunction
|
|||
import importlib
|
||||
|
||||
|
||||
|
||||
class BanditTestSet():
|
||||
|
||||
tests = OrderedDict()
|
||||
|
||||
def __init__(self, logger, test_config):
|
||||
def __init__(self, logger, profile=None):
|
||||
self.logger = logger
|
||||
self.load_tests()
|
||||
filter_list = self._filter_list_from_config(profile=profile)
|
||||
self.load_tests(filter=filter_list)
|
||||
|
||||
def _filter_list_from_config(self, profile=None):
|
||||
# will create an (include,exclude) list tuple from a specified name config
|
||||
# section
|
||||
|
||||
# if a profile isn't set, there is nothing to do here
|
||||
if not profile:
|
||||
return_tuple = ([],[])
|
||||
return return_tuple
|
||||
|
||||
# an empty include list means that all are included
|
||||
include_list = []
|
||||
# profile needs to be a dict, include needs to be an element in profile,
|
||||
# include needs to be a list, and 'all' is not in include
|
||||
if(isinstance(profile, dict) and 'include' in profile and
|
||||
isinstance(profile['include'], list) and
|
||||
not 'all' in profile['include']):
|
||||
# there is a list of specific includes, add them to the include list
|
||||
for inc in profile['include']:
|
||||
include_list.append(inc)
|
||||
|
||||
# an empty exclude list means that none are excluded, an exclude list with
|
||||
# 'all' means that all are excluded. Specifically named excludes are
|
||||
# subtracted from the include list.
|
||||
exclude_list = []
|
||||
if(isinstance(profile, dict) and 'exclude' in profile and
|
||||
isinstance(profile['exclude'], list)):
|
||||
# it's a list, exclude specific tests
|
||||
for exc in profile['exclude']:
|
||||
exclude_list.append(exc)
|
||||
|
||||
return_tuple = (include_list, exclude_list)
|
||||
return return_tuple
|
||||
|
||||
def _filter_tests(self, filter):
|
||||
'''
|
||||
Filters the test set according to the filter tuple which contains
|
||||
include and exclude lists.
|
||||
:param filter: Include, exclude lists tuple
|
||||
:return: -
|
||||
'''
|
||||
include_list = filter[0]
|
||||
exclude_list = filter[1]
|
||||
|
||||
# copy of tests dictionary for removing tests from
|
||||
temp_dict = copy.deepcopy(self.tests)
|
||||
|
||||
# if the include list is empty, we don't have to do anything, if it
|
||||
# isn't, we need to remove all tests except the ones in the list
|
||||
if include_list:
|
||||
for check_type in self.tests:
|
||||
for test_name in self.tests[check_type]:
|
||||
if test_name not in include_list:
|
||||
del temp_dict[check_type][test_name]
|
||||
|
||||
# remove the items specified in exclude list
|
||||
if exclude_list:
|
||||
for check_type in self.tests:
|
||||
for test_name in self.tests[check_type]:
|
||||
if test_name in exclude_list:
|
||||
del temp_dict[check_type][test_name]
|
||||
|
||||
# copy tests back over from temp copy
|
||||
self.tests = copy.deepcopy(temp_dict)
|
||||
|
||||
def _get_decorators_list(self):
|
||||
'''
|
||||
|
@ -48,7 +113,7 @@ class BanditTestSet():
|
|||
return_list.append(d[0])
|
||||
return return_list
|
||||
|
||||
def load_tests(self):
|
||||
def load_tests(self, filter=None):
|
||||
'''
|
||||
Loads all tests from the plugins directory and puts them into the tests
|
||||
dictionary.
|
||||
|
@ -103,6 +168,8 @@ class BanditTestSet():
|
|||
self.tests[check] = {}
|
||||
self.tests[check][function_name] = function
|
||||
|
||||
self._filter_tests(filter)
|
||||
|
||||
def get_tests(self, checktype):
|
||||
'''
|
||||
Returns all tests that are of type checktype
|
||||
|
|
12
main.py
12
main.py
|
@ -20,7 +20,7 @@ import sys
|
|||
import argparse
|
||||
from bandit import manager as b_manager
|
||||
|
||||
default_test_config = 'bandit.ini'
|
||||
default_test_config = 'bandit.yaml'
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
|
@ -36,12 +36,17 @@ if __name__ == '__main__':
|
|||
help='number of context lines to print'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-t', '--testconfig', dest='test_config',
|
||||
'-f', '--configfile', dest='config_file',
|
||||
action='store', default=default_test_config, type=str,
|
||||
help='test config file (default: %s)' % (
|
||||
default_test_config
|
||||
)
|
||||
)
|
||||
parser.add_argument(
|
||||
'-p', '--profile', dest='profile',
|
||||
action='store', default=None, type=str,
|
||||
help='test set profile in config to use'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-l', '--level', dest='level', action='count',
|
||||
default=1, help='results level filter'
|
||||
|
@ -58,7 +63,8 @@ if __name__ == '__main__':
|
|||
|
||||
args = parser.parse_args()
|
||||
|
||||
b_mgr = b_manager.BanditManager(args.test_config, args.debug)
|
||||
b_mgr = b_manager.BanditManager(args.config_file, args.debug,
|
||||
profile_name=args.profile)
|
||||
b_mgr.run_scope(args.files)
|
||||
if args.debug:
|
||||
b_mgr.output_metaast()
|
||||
|
|
Loading…
Reference in New Issue