Repository: PyCQA/bandit (python)

Issue:
One-liner in bandit config to skip B101 assert_used in files matching a filter
Currently you can exclude a directory and you can skip certain tests across all scanned files. It would be very handy to be able to exclude test_* pytest files from the B101 assert test with a simple one liner in the .bandit config file.

This is a feature request for something like:

In .bandit
```
[bandit]
pytest_filter: test_* #excludes B101 in files matching test_*
```

Excluding all B101 is not a good solution, because I want to know about B101 in non-test modules.

Also, I'd like to be able to exclude on file filter like test_* rather than directory test/ because that filter works better across projects I have seen where test directory might not be named consistently, but all the pytests are generally named a special way e.g. test_*

This will allow a more general (across projects) config file to be set up.


Pull request description:
Add skip configuration to assert_used
Adding this configuration allows the user to skip the assert_used check
against some files. This is useful because asserts are very common
in test files when using pytest.

Specifying this configuration:

```
assert_used:
  skips: ['test.py$', '^test']
```

would skip all asserts against a test file.

Resolves #346

Thank you for the awesome project and your time!

Patch:
diff --git a/bandit/core/context.py b/bandit/core/context.py
index 33a1658..da90ed2 100644
--- a/bandit/core/context.py
+++ b/bandit/core/context.py
@@ -314,3 +314,7 @@ class Context(object):
                 if module in imp:
                     return True
         return False
+
+    @property
+    def filename(self):
+        return self._context.get('filename')
diff --git a/bandit/plugins/asserts.py b/bandit/plugins/asserts.py
index b535625..12d1102 100644
--- a/bandit/plugins/asserts.py
+++ b/bandit/plugins/asserts.py
@@ -19,6 +19,16 @@ Please see
 https://docs.python.org/3/reference/simple_stmts.html#the-assert-statement for
 more info on ``assert``
 
+**Config Options:**
+
+You can configure files that skip this check. This is often useful when you
+use assert statements in test cases.
+
+.. code-block:: yaml
+
+    assert_used:
+      skips: ['*_test.py', 'test_*.py']
+
 :Example:
 
 .. code-block:: none
@@ -39,14 +49,25 @@ more info on ``assert``
 .. versionadded:: 0.11.0
 
 """
+import fnmatch
 
 import bandit
 from bandit.core import test_properties as test
 
 
+def gen_config(name):
+    if name == 'assert_used':
+        return {'skips': []}
+
+
+@test.takes_config
 @test.test_id('B101')
 @test.checks('Assert')
-def assert_used(context):
+def assert_used(context, config):
+    for skip in config.get('skips', []):
+        if fnmatch.fnmatch(context.filename, skip):
+            return None
+
     return bandit.Issue(
         severity=bandit.LOW,
         confidence=bandit.HIGH,


Test patch:
diff --git a/tests/functional/test_functional.py b/tests/functional/test_functional.py
index e3b7370..7cccae4 100644
--- a/tests/functional/test_functional.py
+++ b/tests/functional/test_functional.py
@@ -601,6 +601,24 @@ class FunctionalTests(testtools.TestCase):
 
     def test_asserts(self):
         '''Test catching the use of assert.'''
+        test = next((x for x in self.b_mgr.b_ts.tests['Assert']
+                     if x.__name__ == 'assert_used'))
+
+        test._config = {'skips': []}
+        expect = {
+            'SEVERITY': {'UNDEFINED': 0, 'LOW': 1, 'MEDIUM': 0, 'HIGH': 0},
+            'CONFIDENCE': {'UNDEFINED': 0, 'LOW': 0, 'MEDIUM': 0, 'HIGH': 1}
+        }
+        self.check_example('assert.py', expect)
+
+        test._config = {'skips': ['*assert.py']}
+        expect = {
+            'SEVERITY': {'UNDEFINED': 0, 'LOW': 0, 'MEDIUM': 0, 'HIGH': 0},
+            'CONFIDENCE': {'UNDEFINED': 0, 'LOW': 0, 'MEDIUM': 0, 'HIGH': 0}
+        }
+        self.check_example('assert.py', expect)
+
+        test._config = {}
         expect = {
             'SEVERITY': {'UNDEFINED': 0, 'LOW': 1, 'MEDIUM': 0, 'HIGH': 0},
             'CONFIDENCE': {'UNDEFINED': 0, 'LOW': 0, 'MEDIUM': 0, 'HIGH': 1}
diff --git a/tests/unit/core/test_context.py b/tests/unit/core/test_context.py
index bc356a3..e6df83e 100644
--- a/tests/unit/core/test_context.py
+++ b/tests/unit/core/test_context.py
@@ -254,3 +254,12 @@ class ContextTests(testtools.TestCase):
 
         new_context = context.Context()
         self.assertFalse(new_context.is_module_imported_like('spam'))
+
+    def test_filename(self):
+        ref_context = dict(filename='spam.py')
+        new_context = context.Context(context_object=ref_context)
+
+        self.assertEqual(new_context.filename, 'spam.py')
+
+        new_context = context.Context()
+        self.assertIsNone(new_context.filename)