Skip to content

Commit

Permalink
PEP-518 support: configure bandit via pyproject.toml (#401)
Browse files Browse the repository at this point in the history
* parse config from toml

* test toml config parsing

* update docs

* FIX pep8 "line too long" in tests

* review

* +extras

* use setup.cfg for extras

* fix setup.cfg

* fix

* Apply suggestions from code review

Co-authored-by: Lionel Bersee <lionel1232@gmail.com>

* Update doc/source/config.rst

Co-authored-by: Lionel Bersee <lionel1232@gmail.com>

* Update doc/source/config.rst

Co-authored-by: Eric Brown <ericwb@users.noreply.github.com>

* actualize TOML config example in docs

Co-authored-by: Eric Brown <ericwb@users.noreply.github.com>
Co-authored-by: Luke Hinds <7058938+lukehinds@users.noreply.github.com>
Co-authored-by: Lionel Bersee <lionel1232@gmail.com>
  • Loading branch information
4 people committed Aug 24, 2021
1 parent d4faa78 commit 44f5c41
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 12 deletions.
24 changes: 17 additions & 7 deletions bandit/core/config.py
Expand Up @@ -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):
Expand Down
53 changes: 53 additions & 0 deletions doc/source/config.rst
Expand Up @@ -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 <https://www.python.org/dev/peps/pep-0518/>`_ 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
Expand Down
8 changes: 7 additions & 1 deletion setup.cfg
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions test-requirements.txt
Expand Up @@ -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
49 changes: 45 additions & 4 deletions tests/unit/core/test_config.py
Expand Up @@ -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)

Expand Down Expand Up @@ -112,7 +114,7 @@ def test_not_exist(self):


class TestConfigCompat(testtools.TestCase):
sample_yaml = textwrap.dedent("""
sample = textwrap.dedent("""
profiles:
test_1:
include:
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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'

0 comments on commit 44f5c41

Please sign in to comment.