diff --git a/bandit/core/config.py b/bandit/core/config.py index 422432bfb..be42537d2 100644 --- a/bandit/core/config.py +++ b/bandit/core/config.py @@ -36,13 +36,23 @@ def __init__(self, config_file=None): raise utils.ConfigError("Could not read config file.", config_file) - try: - with f: - self._config = yaml.safe_load(f) - self.validate(config_file) - except yaml.YAMLError as err: - LOG.error(err) - raise utils.ConfigError("Error parsing file.", config_file) + if config_file.endswith('.toml'): + import toml + try: + with f: + self._config = toml.load(f)['tool']['bandit'] + except toml.TomlDecodeError as err: + LOG.error(err) + raise utils.ConfigError("Error parsing file.", config_file) + else: + try: + with f: + self._config = yaml.safe_load(f) + except yaml.YAMLError as err: + LOG.error(err) + raise utils.ConfigError("Error parsing file.", config_file) + + self.validate(config_file) # valid config must be a dict if not isinstance(self._config, dict): diff --git a/doc/source/config.rst b/doc/source/config.rst index c092e8ae1..62177dfd9 100644 --- a/doc/source/config.rst +++ b/doc/source/config.rst @@ -37,6 +37,59 @@ several config files and pick from them using `-c`. If you only wish to control the specific tests that are to be run (and not their parameters) then using `-s` or `-t` on the command line may be more appropriate. +Also you can configure bandit via +`pyproject.toml `_ file. In this +case you would explicitly specify the path to configuration via `-c` too. +For example: + +.. code-block:: TOML + + [tool.bandit] + tests = ["B201", "B301"] + skips = ["B101", "B601"] + + [tool.bandit.any_other_function_with_shell_equals_true] + no_shell = [ + "os.execl", + "os.execle", + "os.execlp", + "os.execlpe", + "os.execv", + "os.execve", + "os.execvp", + "os.execvpe", + "os.spawnl", + "os.spawnle", + "os.spawnlp", + "os.spawnlpe", + "os.spawnv", + "os.spawnve", + "os.spawnvp", + "os.spawnvpe", + "os.startfile" + ] + shell = [ + "os.system", + "os.popen", + "os.popen2", + "os.popen3", + "os.popen4", + "popen2.popen2", + "popen2.popen3", + "popen2.popen4", + "popen2.Popen3", + "popen2.Popen4", + "commands.getoutput", + "commands.getstatusoutput" + ] + subprocess = [ + "subprocess.Popen", + "subprocess.call", + "subprocess.check_call", + "subprocess.check_output" + ] + + Skipping Tests -------------- The bandit config may contain optional lists of test IDs to either include diff --git a/setup.cfg b/setup.cfg index 9631350ce..342f46446 100644 --- a/setup.cfg +++ b/setup.cfg @@ -27,7 +27,13 @@ classifier = project_urls = Release notes = https://github.com/PyCQA/bandit/releases -[entry_points] +[options.extras_require] +yaml = + PyYAML +toml = + toml + +[options.entry_points] console_scripts = bandit = bandit.cli.main:main bandit-config-generator = bandit.cli.config_generator:main diff --git a/test-requirements.txt b/test-requirements.txt index 3bc2e9665..51868f6a1 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -7,5 +7,6 @@ hacking>=2.0.0 # Apache-2.0 stestr>=2.5.0 # Apache-2.0 testscenarios>=0.5.0 # Apache-2.0/BSD testtools>=2.3.0 # MIT +toml # MIT beautifulsoup4>=4.8.0 # MIT pylint==1.9.4 # GPLv2 diff --git a/tests/unit/core/test_config.py b/tests/unit/core/test_config.py index 6a2e4e834..08a28e321 100644 --- a/tests/unit/core/test_config.py +++ b/tests/unit/core/test_config.py @@ -16,14 +16,16 @@ class TempFile(fixtures.Fixture): - def __init__(self, contents=None): + def __init__(self, contents=None, suffix='.yaml'): super(TempFile, self).__init__() self.contents = contents + self.suffix = suffix def setUp(self): super(TempFile, self).setUp() - with tempfile.NamedTemporaryFile(mode='wt', delete=False) as f: + with tempfile.NamedTemporaryFile(suffix=self.suffix, mode='wt', + delete=False) as f: if self.contents: f.write(self.contents) @@ -112,7 +114,7 @@ def test_not_exist(self): class TestConfigCompat(testtools.TestCase): - sample_yaml = textwrap.dedent(""" + sample = textwrap.dedent(""" profiles: test_1: include: @@ -157,10 +159,11 @@ class TestConfigCompat(testtools.TestCase): level: HIGH message: "{module} is considered insecure." """) + suffix = '.yaml' def setUp(self): super(TestConfigCompat, self).setUp() - f = self.useFixture(TempFile(self.sample_yaml)) + f = self.useFixture(TempFile(self.sample, suffix=self.suffix)) self.config = config.BanditConfig(f.name) def test_converted_include(self): @@ -252,3 +255,41 @@ def test_bad_yaml(self): self.config = config.BanditConfig(f.name) except utils.ConfigError as e: self.assertIn("Error parsing file.", e.message) + + +class TestTomlConfig(TestConfigCompat): + sample = textwrap.dedent(""" + [tool.bandit.profiles.test_1] + include = [ + "any_other_function_with_shell_equals_true", + "assert_used", + ] + + [tool.bandit.profiles.test_2] + include = ["blacklist_calls"] + + [tool.bandit.profiles.test_3] + include = ["blacklist_imports"] + + [tool.bandit.profiles.test_4] + exclude = ["assert_used"] + + [tool.bandit.profiles.test_5] + exclude = ["blacklist_calls", "blacklist_imports"] + + [tool.bandit.profiles.test_6] + include = ["blacklist_calls"] + exclude = ["blacklist_imports"] + + [[tool.bandit.blacklist_calls.bad_name_sets]] + [tool.bandit.blacklist_calls.bad_name_sets.pickle] + qualnames = ["pickle.loads"] + message = "{func} library appears to be in use." + + [[tool.bandit.blacklist_imports.bad_import_sets]] + [tool.bandit.blacklist_imports.bad_import_sets.telnet] + imports = ["telnetlib"] + level = "HIGH" + message = "{module} is considered insecure." + """) + suffix = '.toml'