deb-bandit/bandit/core/test_set.py

124 lines
4.5 KiB
Python

# -*- 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 importlib
import logging
from bandit.core import blacklisting
from bandit.core import extension_loader
LOG = logging.getLogger(__name__)
class BanditTestSet(object):
def __init__(self, config, profile=None):
if not profile:
profile = {}
extman = extension_loader.MANAGER
filtering = self._get_filter(config, profile)
self.plugins = [p for p in extman.plugins
if p.plugin._test_id in filtering]
self.plugins.extend(self._load_builtins(filtering, profile))
self._load_tests(config, self.plugins)
@staticmethod
def _get_filter(config, profile):
extman = extension_loader.MANAGER
inc = set(profile.get('include', []))
exc = set(profile.get('exclude', []))
all_blacklist_tests = set()
for _node, tests in extman.blacklist.items():
all_blacklist_tests.update(t['id'] for t in tests)
# this block is purely for backwards compatibility, the rules are as
# follows:
# B001,B401 means B401
# B401 means B401
# B001 means all blacklist tests
if 'B001' in inc:
if not inc.intersection(all_blacklist_tests):
inc.update(all_blacklist_tests)
inc.discard('B001')
if 'B001' in exc:
if not exc.intersection(all_blacklist_tests):
exc.update(all_blacklist_tests)
exc.discard('B001')
if inc:
filtered = inc
else:
filtered = set(extman.plugins_by_id.keys())
filtered.update(extman.builtin)
filtered.update(all_blacklist_tests)
return filtered - exc
def _load_builtins(self, filtering, profile):
'''loads up builtin functions, so they can be filtered.'''
class Wrapper(object):
def __init__(self, name, plugin):
self.name = name
self.plugin = plugin
extman = extension_loader.MANAGER
blacklist = profile.get('blacklist')
if not blacklist: # not overridden by legacy data
blacklist = {}
for node, tests in extman.blacklist.items():
values = [t for t in tests if t['id'] in filtering]
if values:
blacklist[node] = values
if not blacklist:
return []
# this dresses up the blacklist to look like a plugin, but
# the '_checks' data comes from the blacklist information.
# the '_config' is the filtered blacklist data set.
setattr(blacklisting.blacklist, "_test_id", 'B001')
setattr(blacklisting.blacklist, "_checks", blacklist.keys())
setattr(blacklisting.blacklist, "_config", blacklist)
return [Wrapper('blacklist', blacklisting.blacklist)]
def _load_tests(self, config, plugins):
'''Builds a dict mapping tests to node types.'''
self.tests = {}
for plugin in plugins:
if hasattr(plugin.plugin, '_takes_config'):
# TODO(??): config could come from profile ...
cfg = config.get_option(plugin.plugin._takes_config)
if cfg is None:
genner = importlib.import_module(plugin.plugin.__module__)
cfg = genner.gen_config(plugin.plugin._takes_config)
plugin.plugin._config = cfg
for check in plugin.plugin._checks:
self.tests.setdefault(check, []).append(plugin.plugin)
LOG.debug('added function %s (%s) targeting %s',
plugin.name, plugin.plugin._test_id, check)
def get_tests(self, checktype):
'''Returns all tests that are of type checktype
:param checktype: The type of test to filter on
:return: A list of tests which are of the specified type
'''
return self.tests.get(checktype) or []