From 20c12cde76b705ac27d6e4e34725e90afc0a91ff Mon Sep 17 00:00:00 2001 From: "Dave Walker (Daviey)" Date: Fri, 17 Jul 2015 16:39:46 +0100 Subject: [PATCH] Register plugins included as entry-points Previously, we tried to load the default plugins by using the path to bandit to import them dynamically and load them. This is unreliable on systems where the default path and the actual path are different depending on how bandit is installed. By using entry-points, we can completely side-step this problem. _______ < FIXED > ------- \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || Closes-Bug: #1475681 Co-Authored-By: Ian Cordasco Signed-off-by: Dave Walker (Daviey) Change-Id: I19eed3b312c921e98bdabfb346cddd406186d963 --- bandit/core/config.py | 8 --- bandit/core/constants.py | 7 --- bandit/core/test_set.py | 114 +++++++++------------------------------ setup.cfg | 84 +++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 104 deletions(-) diff --git a/bandit/core/config.py b/bandit/core/config.py index fd629f41..5de7e7fd 100644 --- a/bandit/core/config.py +++ b/bandit/core/config.py @@ -103,7 +103,6 @@ class BanditConfig(): ''' self._init_progress_increment() self._init_output_colors() - self._init_plugins_dir() self._init_plugin_name_pattern() def _init_progress_increment(self): @@ -148,13 +147,6 @@ class BanditConfig(): settings_string = 'color_' + color self._settings[settings_string] = color_settings[color] - def _init_plugins_dir(self): - '''Sets settings['plugins_dir'] from default or config file.''' - plugins_dir = constants.plugins_dir - if self.get_option('plugins_dir'): - plugins_dir = self.get_option('plugins_dir') - self._settings['plugins_dir'] = plugins_dir - def _init_plugin_name_pattern(self): '''Sets settings['plugin_name_pattern'] from default or config file.''' plugin_name_pattern = constants.plugin_name_pattern diff --git a/bandit/core/constants.py b/bandit/core/constants.py index 9be43224..d4a89911 100644 --- a/bandit/core/constants.py +++ b/bandit/core/constants.py @@ -14,10 +14,6 @@ # License for the specific language governing permissions and limitations # under the License. -from distutils.sysconfig import get_python_lib -import os - - # default output text colors color = { 'DEFAULT': '\033[0m', @@ -33,9 +29,6 @@ plugin_name_pattern = '*.py' # default progress increment progress_increment = 50 -# default plugins dir -plugins_dir = os.path.join(get_python_lib(), 'bandit', 'plugins') - # flag/s used to mark lines where identified issues should not be reported SKIP_FLAGS = ['nosec', ] diff --git a/bandit/core/test_set.py b/bandit/core/test_set.py index 79bc0944..d93f6ba2 100644 --- a/bandit/core/test_set.py +++ b/bandit/core/test_set.py @@ -17,11 +17,6 @@ from collections import OrderedDict import copy -import glob -import importlib -from inspect import getmembers -from inspect import isfunction -import os import sys @@ -107,97 +102,38 @@ class BanditTestSet(): for k in self.tests: self.logger.debug('\t%s : %s', k, self.tests[k]) - def _get_decorators_list(self): - '''Returns a list of decorator function names - - Returns a list of decorator function names so that they can be - ignored when discovering test function names. - ''' - - # we need to know the name of the decorators so we can automatically - # ignore them when discovering functions - decorator_source_file = "bandit.core.test_properties" - module = importlib.import_module(decorator_source_file) - - return_list = [] - decorators = [o for o in getmembers(module) if isfunction(o[1])] - for d in decorators: - return_list.append(d[0]) - self.logger.debug('_get_decorators_list returning: %s', return_list) - return return_list + def _get_extension_manager(self): + from bandit.core import extension_loader + return extension_loader.MANAGER def load_tests(self, filter=None): '''Loads all tests in the plugins directory into testsdictionary.''' - - # tests are a dictionary of functions, grouped by check type - # where the key is the function name, and the value is the - # function itself. - # eg. tests[check_type][fn_name] = function self.tests = dict() - directory = self.config.get_setting('plugins_dir') - plugin_name_pattern = self.config.get_setting('plugin_name_pattern') + extmgr = self._get_extension_manager() - decorators = self._get_decorators_list() - # try to import each python file in the plugins directory - sys.path.append(os.path.dirname(directory)) - for file in glob.glob1(directory, plugin_name_pattern): - module_name = os.path.basename(file).split('.')[0] - - # try to import the module by name - try: - outer = os.path.basename(os.path.normpath(directory)) - self.logger.debug("importing plugin module: %s", - outer + '.' + module_name) - module = importlib.import_module(outer + '.' + module_name) - - # if it fails, die - except ImportError as e: - self.logger.error("could not import plugin module '%s.%s'", - directory, module_name) - self.logger.error("\tdetail: '%s'", str(e)) - sys.exit(2) - - # otherwise we want to obtain a list of all functions in the module - # and add them to our dictionary of tests - else: - functions_list = [ - o for o in getmembers(module) if isfunction(o[1]) - ] - for cur_func in functions_list: - # for every function in the module, add to the dictionary - # unless it's one of our decorators, then ignore it - fn_name = cur_func[0] - if fn_name not in decorators: - try: - function = getattr(module, fn_name) - except AttributeError as e: - self.logger.error( - "could not locate test function '%s' in " - "module '%s.%s'", - fn_name, directory, module_name + for plugin in extmgr.plugins: + fn_name = plugin.name + function = plugin.plugin + if hasattr(function, '_checks'): + for check in function._checks: + # if check type hasn't been encountered + # yet, initialize to empty dictionary + if check not in self.tests: + self.tests[check] = {} + # if there is a test name collision, bail + if fn_name in self.tests[check]: + self.logger.error( + "Duplicate function definition " + "%s in %s", fn_name, file + ) + sys.exit(2) + else: + self.tests[check][fn_name] = function + self.logger.debug( + 'added function %s targetting %s', + fn_name, check ) - sys.exit(2) - else: - if hasattr(function, '_checks'): - for check in function._checks: - # if check type hasn't been encountered - # yet, initialize to empty dictionary - if check not in self.tests: - self.tests[check] = {} - # if there is a test name collision, bail - if fn_name in self.tests[check]: - self.logger.error( - "Duplicate function definition " - "%s in %s", fn_name, file - ) - sys.exit(2) - else: - self.tests[check][fn_name] = function - self.logger.debug( - 'added function %s targetting %s', - fn_name, check - ) self._filter_tests(filter) def get_tests(self, checktype): diff --git a/setup.cfg b/setup.cfg index b8fd6df8..be904f1e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,6 +28,90 @@ bandit.formatters = json = bandit.core.formatters:report_json txt = bandit.core.formatters:report_text xml = bandit.core.formatters:report_xml +bandit.plugins = + # bandit/plugins/asserts.py + assert_used = bandit.plugins.asserts:assert_used + + # bandit/plugins/blacklist_calls.py + blacklist_calls = bandit.plugins.blacklist_calls:blacklist_calls + + # bandit/plugins/blacklist_imports.py + blacklist_imports = bandit.plugins.blacklist_imports:blacklist_imports + blacklist_import_func = bandit.plugins.blacklist_imports:blacklist_import_func + + # bandit/plugins/crypto_request_no_cert_validation.py + request_with_no_cert_validation = bandit.plugins.crypto_request_no_cert_validation:request_with_no_cert_validation + + # bandit/plugins/exec_as_root.py + execute_with_run_as_root_equals_true = bandit.plugins.exec_as_root:execute_with_run_as_root_equals_true + + # bandit/plugins/exec.py + exec_used = bandit.plugins.exec:exec_used + + # bandit/plugins/general_bad_File_permissions.py + set_bad_file_permissions = bandit.plugins.general_bad_file_permissions:set_bad_file_permissions + + # bandit/plugins/general_bind_all_interfaces.py + hardcoded_bind_all_interfaces = bandit.plugins.general_bind_all_interfaces:hardcoded_bind_all_interfaces + + # bandit/plugins/general_hardcoded_password.py + hardcoded_password = bandit.plugins.general_hardcoded_password:hardcoded_password + + # bandit/plugins/general_hardcoded_tmp.py + hardcoded_tmp_directory = bandit.plugins.general_hardcoded_tmp:hardcoded_tmp_directory + + # bandit/plugins/injection_paramiko.py + paramiko_calls = bandit.plugins.injection_paramiko:paramiko_calls + + # bandit/plugins/injection_shell.py + subprocess_popen_with_shell_equals_true = bandit.plugins.injection_shell:subprocess_popen_with_shell_equals_true + subprocess_without_shell_equals_true = bandit.plugins.injection_shell:subprocess_without_shell_equals_true + any_other_function_with_shell_equals_true = bandit.plugins.injection_shell:any_other_function_with_shell_equals_true + start_process_with_a_shell = bandit.plugins.injection_shell:start_process_with_a_shell + start_process_with_no_shell = bandit.plugins.injection_shell:start_process_with_no_shell + start_process_with_partial_path = bandit.plugins.injection_shell:start_process_with_partial_path + + # bandit/plugins/injection_sql.py + hardcoded_sql_expressions = bandit.plugins.injection_sql:hardcoded_sql_expressions + + # bandit/plugins/injection_wildcard.py + linux_commands_wildcard_injection = bandit.plugins.injection_wildcard:linux_commands_wildcard_injection + + # bandit/plugins/insecure_ssl_tls.py + ssl_with_bad_version = bandit.plugins.insecure_ssl_tls:ssl_with_bad_version + ssl_with_bad_defaults = bandit.plugins.insecure_ssl_tls:ssl_with_bad_defaults + ssl_with_no_version = bandit.plugins.insecure_ssl_tls:ssl_with_no_version + + # bandit/plugins/jinja2_templates.py + jinja2_autoescape_false = bandit.plugins.jinja2_templates:jinja2_autoescape_false + + # bandit/plugins/mako_templates.py + use_of_mako_templates = bandit.plugins.mako_templates:use_of_mako_templates + + # bandit/plugins/secret_config_options.py + password_config_option_not_marked_secret = bandit.plugins.secret_config_option:password_config_option_not_marked_secret + + # bandit/plugins/try_except_pass.py + try_except_pass = bandit.plugins.try_except_pass:try_except_pass + + # bandit/plugins/xlm.py + etree_celement_function_calls = bandit.plugins.xml:etree_celement_function_calls + etree_element_function_calls = bandit.plugins.xml:etree_element_function_calls + expatreader_function_calls = bandit.plugins.xml:expatreader_function_calls + expatbuilder_function_calls = bandit.plugins.xml:expatbuilder_function_calls + sax_function_calls = bandit.plugins.xml:sax_function_calls + minidom_function_calls = bandit.plugins.xml:minidom_function_calls + pulldom_function_calls = bandit.plugins.xml:pulldom_function_calls + lxml_function_calls = bandit.plugins.xml:lxml_function_calls + etree_celement_import = bandit.plugins.xml:etree_celement_import + etree_element_import = bandit.plugins.xml:etree_element_import + expatreader_import = bandit.plugins.xml:expatreader_import + expatbuilder_import = bandit.plugins.xml:expatbuilder_import + sax_import = bandit.plugins.xml:sax_import + minidom_import = bandit.plugins.xml:minidom_import + pulldom_import = bandit.plugins.xml:pulldom_import + xmlrpclib_import = bandit.plugins.xml:xmlrpclib_import + lxml_import = bandit.plugins.xml:lxml_import [files] package_data =